Add error handling for IPython display errors in notebook cell execution#12238
Add error handling for IPython display errors in notebook cell execution#12238
Conversation
|
@cscheid thanks again for the live review. I was indeed missing a test and not handling cell level precedence. This is now fixed, and test added. |
|
I tried to re-run great_tables example using v 0.16.0 and I noticed truncated output, just above the quarto error and stacktrace. So I am trying to understand this. |
|
An another mystery as this is not happening in VSCODE debug mode Full Log
|
36d4ef4 to
348241e
Compare
|
So I don't manage to solve the truncated console output for long error message from python. This does not happen in the log file when setting I believe what happens is something about writing to console and buffer size. It seems there is a limit where msg needs to be written by chunk. So if we manage to write by chunk, we may solve this. Also I noticed a difference:
We also may want to fix this. I am thinking we should merge this, and then handle in another issue the long output error truncation. |
|
I am now thinking this is about Lines 158 to 162 in e7809ce which is not writing the full buffer in a single call as the doc says 🤔 |
|
Yes this is it !! It will be hard to test though 🤔 But here is the output now
> quarto render index.qmd --execute-daemon-restart --log test.log
Check file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/quarto.ts
Starting python3 kernel...Done
Executing 'index.quarto_ipynb'
Cell 1/1: ''...ERROR:
An error occurred while executing the following cell:
------------------
from great_tables import GT, nanoplot_options
import polars as pl
weather_2 = pl.DataFrame(
{
"station": ["Station " + str(x) for x in range(1, 4)],
"temperatures": [
{
"x": [6.1, 8.0, 10.1, 10.5, 11.2, 12.4, 13.1, 15.3],
"y": [24.2, 28.2, 30.2, 30.5, 30.5, 33.1, 33.5, 32.7],
},
{
"x": [7.1, 8.2, 10.3, 10.75, 11.25, 12.5, 13.5, 14.2],
"y": [18.2, 18.1, 20.3, 20.5, 21.4, 21.9, 23.1, 23.3],
},
{
"x": [6.3, 7.1, 10.3, 11.0, 12.07, 13.1, 15.12, 16.42],
"y": [15.2, 17.77, 21.42, 21.63, 25.23, 26.84, 27.2, 27.44],
},
]
}
)
(
GT(weather_2)
.fmt_nanoplot(
columns="temperatures",
plot_type="line",
expand_x=[5, 16],
expand_y=[10, 40],
options=nanoplot_options(
show_data_area=False,
show_data_line=False
)
)
)
------------------
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\IPython\core\formatters.py:406, in BaseFormatter.__call__(self, obj)
404 method = get_real_method(obj, self.print_method)
405 if method is not None:
--> 406 return method()
407 return None
408 else:
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\gt.py:293, in GT._repr_html_(self)
290 make_page = defaults["make_page"]
291 all_important = defaults["all_important"]
--> 293 rendered = self.as_raw_html(
294 make_page=make_page,
295 all_important=all_important,
296 )
298 return rendered
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\_export.py:220, in as_raw_html(self, inline_css, make_page, all_important)
130 def as_raw_html(
131 self: GT,
132 inline_css: bool = False,
133 make_page: bool = False,
134 all_important: bool = False,
135 ) -> str:
136 """
137 Get the HTML content of a GT object.
138
(...) 218 ```
219 """
--> 220 built_table = self._build_data(context="html")
222 if not inline_css:
223 html_table = built_table._render_as_html(
224 make_page=make_page,
225 all_important=all_important,
226 )
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\gt.py:311, in GT._build_data(self, context)
308 def _build_data(self, context: str) -> Self:
309 # Build the body of the table by generating a dictionary
310 # of lists with cells initially set to nan values
--> 311 built = self._render_formats(context)
313 if context == "latex":
314 built = _migrate_unformatted_to_output(
315 data=built, data_tbl=self._tbl_data, formats=self._formats, context=context
316 )
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\gt.py:304, in GT._render_formats(self, context)
301 new_body = self._body.copy()
303 # TODO: this body method performs a mutation. Should we make a copy of body?
--> 304 new_body.render_formats(self._tbl_data, self._formats, context)
305 new_body.render_formats(self._tbl_data, self._substitutions, context)
306 return self._replace(_body=new_body)
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\_gt_data.py:172, in Body.render_formats(self, data_tbl, formats, context)
170 raise Exception("Internal Error")
171 for col, row in fmt.cells.resolve():
--> 172 result = eval_func(_get_cell(data_tbl, row, col))
173 if isinstance(result, FormatterSkipElement):
174 continue
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\_formats.py:4716, in fmt_nanoplot.<locals>.fmt_nanoplot_fn(x, context, plot_type, plot_height, missing_vals, reference_line, reference_area, all_single_y_vals, options_plots)
4713 y_vals = x
4714 x_vals = None
-> 4716 nanoplot = _generate_nanoplot(
4717 y_vals=y_vals,
4718 y_ref_line=reference_line,
4719 y_ref_area=reference_area,
4720 x_vals=x_vals,
4721 expand_x=expand_x,
4722 expand_y=expand_y,
4723 missing_vals=missing_vals,
4724 all_single_y_vals=all_single_y_vals,
4725 plot_type=plot_type,
4726 svg_height=plot_height,
4727 **options_plots,
4728 )
4730 return nanoplot
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\_utils_nanoplots.py:915, in _generate_nanoplot(y_vals, y_ref_line, y_ref_area, x_vals, expand_x, expand_y, missing_vals, all_y_vals, all_single_y_vals, plot_type, data_line_type, currency, y_val_fmt_fn, y_axis_fmt_fn, y_ref_line_fmt_fn, data_point_radius, data_point_stroke_color, data_point_stroke_width, data_point_fill_color, data_line_stroke_color, data_line_stroke_width, data_area_fill_color, data_bar_stroke_color, data_bar_stroke_width, data_bar_fill_color, data_bar_negative_stroke_color, data_bar_negative_stroke_width, data_bar_negative_fill_color, reference_line_color, reference_area_fill_color, vertical_guide_stroke_color, vertical_guide_stroke_width, show_data_points, show_data_line, show_data_area, show_reference_line, show_reference_area, show_vertical_guides, show_y_axis_guide, interactive_data_values, svg_height)
911 # If x values are present then normalize them between [0, 1]; if
912 # there are no x values, generate equally-spaced x values according
913 # to the number of y values
914 if plot_type == "line" and x_vals is not None:
--> 915 if expand_x is not None and _val_is_str(expand_x):
916 # TODO: the line below lacked tests, and called non-existent methods.
917 # replace with something that doesn't use pandas and returns the correct thing.
918
919 # Assume that string values are dates and convert them to timestamps
920 # expand_x = pd.to_datetime(expand_x, utc=True).timestamp()
921 raise NotImplementedError("Currently, passing expand_x as a string is unsupported.")
923 # Scale to proportional values
File ~\Documents\DEV_OTHER\00-TESTS\test-quarto\.venv\Lib\site-packages\great_tables\_utils_nanoplots.py:41, in _val_is_str(x)
39 # If a list then signal a failure
40 if isinstance(x, list):
---> 41 raise ValueError("The input cannot be a list. It must be a single value.")
43 return isinstance(x, (str))
ValueError: The input cannot be a list. It must be a single value.
ERROR: Error
at renderFiles (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/render-files.ts:351:23)
at eventLoopTick (ext:core/01_core.js:175:7)
at async renderProject (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/project.ts:463:23)
at async Command.actionHandler (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/command/render/cmd.ts:251:26)
at async Command.execute (https://deno.land/x/cliffy@v1.0.0-rc.3/command/command.ts:1948:7)
at async Command.parseCommand (https://deno.land/x/cliffy@v1.0.0-rc.3/command/command.ts:1780:14)
at async quarto (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/quarto.ts:191:5)
at async file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/quarto.ts:220:5
at async file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/core/main.ts:41:14
at async mainRunner (file:///C:/Users/chris/Documents/DEV_R/quarto-cli/src/core/main.ts:43:5) |
…ne go for very long message Error in Jupyter Notebook can have very long backtrace which where truncated in console logger, but not in file logger. Context #12238
|
This is better overall to handle error in Jupyter as other error, but we did use However, quarto-cli/src/resources/jupyter/log.py Lines 43 to 45 in ec6cfd0 Though I see now that this output we had for each is coming from quarto-cli/src/resources/jupyter/notebook.py Lines 326 to 328 in ec6cfd0 quarto-cli/src/resources/jupyter/notebook.py Lines 277 to 284 in ec6cfd0 So there is also I broke with Status 🤔 🤦♂️ I should have split this PR in several I think |
|
And this is because we stream status to quarto-cli/src/resources/jupyter/jupyter.py Lines 251 to 254 in ec6cfd0 And we do the same for errors quarto-cli/src/resources/jupyter/jupyter.py Lines 226 to 246 in ec6cfd0 We do it for some warnings too: quarto-cli/src/resources/jupyter/notebook.py Lines 687 to 694 in ec6cfd0 quarto-cli/src/resources/jupyter/notebook.py Lines 751 to 760 in ec6cfd0 So I think all this digging to understand is not vain: My initial fix is probably not completely correct. Correct solution: Use quarto-cli/src/resources/jupyter/log.py Lines 40 to 41 in ec6cfd0 Like we do in two places quarto-cli/src/resources/jupyter/jupyter.py Lines 189 to 195 in ec6cfd0 quarto-cli/src/resources/jupyter/jupyter.py Lines 288 to 290 in ec6cfd0 More work for tomorrow... |
|
So I don't manage to get to the bottom of how the stderr and other stream are redirected in Jupyter context. What I see is that, this is different between two mode
So this PR is good now, but the error message from the new exception raised won't be shown in log file. |
…ution `nbclient` does not error out when a cell has error due to display error. This PR adds error handling for display errors in notebook cell so that `quarto render` will error out. `error: true` control the behavior to allow or not the error.
Using something like
```
_quarto:
tests:
html:
shouldError: default
postRenderCleanup:
- '${input_stem}.quarto_ipynb'
```
would clean the intermediate file , which is not cleaned by default as render is erroring.
and tweak `ensureHtmlElementContents` to use named option in YAML
…ne go for very long message Error in Jupyter Notebook can have very long backtrace which where truncated in console logger, but not in file logger. Context #12238
…nelKeepalive()" This reverts commit dd9a6bd.
58200c4 to
a0efdf9
Compare
Note
This is only a proposal and this needs to be discussed.
Closes #12228
With this change,
quarto renderwould fail for any error not caught bynbclientas cell execution error.nbclientdoes not error out when a cell has an error due to a display error. This PR adds error handling for display errors in notebook cells so thatquarto renderwill error out.error: truecontrols the behavior to allow or not allow the error.Tests are added to shown the new behavior.
We may not want that solution but this is probably coherent with
errorconfig as true or false expectation.If we do not do this, I think it is still not good to output the display error as a cell error without a way to remove it. We could also tweak our processing so that we remove this output in some situations and show the display error as a warning.
This PR comes with a few tweaks to
smoke-allto be able to do more from in-document testing specification and also have a better configuration with a name (so YAML validation for tests will become a reality at some point. I am making the changes slowly across our tests)