Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Make execute preprocessor traits configurable #1690

Merged
merged 5 commits into from Mar 23, 2023
Merged

Conversation

tmetzl
Copy link
Contributor

@tmetzl tmetzl commented Oct 24, 2022

The following traits are not configurable:

interrupt_on_timeout = Bool(True)
allow_errors = Bool(True)
raise_on_iopub_timeout = Bool(True)
timeout = Integer(30)

This means you can't adjust the timeout via nbgrader autograde <My_Assignment> --Execute.timeout=120. This will throw an error "[AutogradeApp | WARNING] Config option `timeout` not recognized by `Execute`. Did you mean one of: `iopub_timeout, startup_timeout, timeout_func`?"

By making the traits configurable the command line or config file option is used correctly.

@github-actions
Copy link
Contributor

Binder 👈 Launch a Binder on branch tmetzl/nbgrader/patch-2

@brichet
Copy link
Contributor

brichet commented Nov 10, 2022

Thanks @tmetzl.

Could you also add help messages ?

@tmetzl
Copy link
Contributor Author

tmetzl commented Nov 10, 2022

I copied the original help messages from nbclient

@brichet
Copy link
Contributor

brichet commented Nov 30, 2022

Thinking again about this, I'm not sure about these changes.

Some of these parameters need to be set:

  • allow_errors = Bool(True) is necessary to execute the whole Notebook when validating or auto-grading. Setting it to False will break validation and auto-grading process.
  • raise_on_iopub_timeout = Bool(True) has been included in 9d0f32d, related to nbconvert dependency
  • timeout = Integer(30) has been introduced in Upgrade nbconvert #1567, and as far as I remember it can be change but should not be set to None. Sometime the kernel didn't respond (i don't remember the reason) and nbgrader waited endlessly.

@tmetzl
Copy link
Contributor Author

tmetzl commented Nov 30, 2022

This PR is not changing any of the default values. It just makes them configurable. I don't see harm in letting the user override the sensible default values picked here. What I really need is the ability to configure the timeout.

Currently I have to use my own ExecutePreprocessor since otherwise everything times out in 30s which is not enough time to autograde my students.

@brichet
Copy link
Contributor

brichet commented Nov 30, 2022

I agree that the timeout should be configurable.

I remember why it was implemented the first time. It is necessary when the cell contains code to redirect the traceback (eg. https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt.ipynb). In this specific case, the execution never ended, and the timeout is necessary.

Perhaps we could add a minimal value > 0 and do not allow none value to guarantee a positive timeout.

shreve added a commit to shreve/ipython that referenced this pull request Feb 8, 2023
The nbgrader project is aware of a form of cheating where students
disrupt `InteractiveShell.showtraceback` in hopes of hiding exceptions
to avoid losing points. They have implemented a solution to prevent this
cheating from working on the client side, and have some tests to
demonstrate this technique:

https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt.ipynb
https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt-alternative.ipynb

In essence, these attacks import the interactive shell and erase the
traceback handler so that their failing tests won't report failures.

    import IPython.core.interactiveshell
    IPython.core.interactiveshell.InteractiveShell.showtraceback = None

The problem is that this causes an exception inside the kernel, leading
to a stalled execution. The kernel has stopped working, but the client
continues to wait for messages. So far, nbgrader's solution to this is
to require a timeout value so the client can eventually decide it is
done. This prevents allowing a value of `None` for `Execute.timeout`
because this would cause a test case to infinitely hang.

This commit addresses the problem by making `InteractiveShell._run_cell`
a little more protective around it's call to `showtraceback()`. There is
already a try/except block around running the cell. This commit adds a
finally clause so that the method will _always_ return an
`ExecutionResult`, even if a new exception is thrown within the except
clause. For the record, the exception thrown is:

    TypeError: 'NoneType' object is not callable

Accepting this change will allow nbgrader to update
`nbgrader.preprocessors.Execute` to support a type of
`Integer(allow_none=True)` as the parent `NotebookClient` intended.

Discussion about this is ongoing in jupyter/nbgrader#1690.
@shreve
Copy link
Contributor

shreve commented Feb 8, 2023

In this specific case, the execution never ended, and the timeout is necessary.

I was looking into why this is the case, and I realized it doesn't have to be. This attack causes an unhandled exception in the kernel, which is why execution hangs indefinitely. I just issued a PR upstream in iPython to handle (read: ignore) that exception so we can still return a result.

I tested this alongside allowing Execute.timeout = None and the tests pass!

I have no idea how long that will take to merge upstream, but once it does, I think we're safe on that front. The upstream nbclient.NotebookClient.timeout is Integer(allow_none=True) so I don't think this is too wild of an idea.

def on_cell_executed(self, **kwargs):
@validate('timeout')
def _validate_timeout(self, proposal):
value = int(proposal['value'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This current validation throws an exception when the value is None.

"""
The time to wait (in seconds) for output from executions.
If a cell execution takes longer, a TimeoutError is raised.
``None`` or ``-1`` will disable the timeout. If ``timeout_func`` is set,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be my preference to only support None and positive integers. If that's the case, this docstring needs to be updated. If not, more code changes are needed.

@shreve
Copy link
Contributor

shreve commented Feb 8, 2023

Thanks for your work on this so far, @tmetzl !

Carreau added a commit to ipython/ipython that referenced this pull request Feb 10, 2023
The nbgrader project is aware of a form of cheating where students
disrupt `InteractiveShell.showtraceback` in hopes of hiding exceptions
to avoid losing points. They have implemented a solution to prevent this
cheating from working on the client side, and have some tests to
demonstrate this technique:


https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt.ipynb
https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt-alternative.ipynb

In essence, these attacks import the interactive shell and erase the
traceback handler so that their failing tests won't report failures.

```python
import IPython.core.interactiveshell
IPython.core.interactiveshell.InteractiveShell.showtraceback = None
```

The problem is that this causes an exception inside the kernel, leading
to a stalled execution. The kernel has stopped working, but the client
continues to wait for messages. So far, nbgrader's solution to this is
to require a timeout value so the client can eventually decide it is
done. This prevents allowing a value of `None` for `Execute.timeout`
because this would cause a test case to infinitely hang.

This commit addresses the problem by making `InteractiveShell._run_cell`
a little more protective around it's call to `showtraceback()`. There is
already a try/except block around running the cell. This commit adds a
finally clause so that the method will _always_ return an
`ExecutionResult`, even if a new exception is thrown within the except
clause. For the record, the exception thrown is:

    TypeError: 'NoneType' object is not callable

Accepting this change will allow nbgrader to update
`nbgrader.preprocessors.Execute` to support a type of
`Integer(allow_none=True)` as the parent `NotebookClient` intended.

Discussion about this is ongoing in jupyter/nbgrader#1690.
@brichet
Copy link
Contributor

brichet commented Mar 6, 2023

Thanks @shreve, it works well with ipython>=8.10.0.
Maybe we could:

  • add a dependency to ipython>=8.10.0
  • remove the traits timeout (and the associated validate decorator), since any value should be allowed now.

I'm still confused about the value of being able to set allow-errors=False, as it would break some functionalities, but maybe I missed a point on that.

@tmetzl
Copy link
Contributor Author

tmetzl commented Mar 6, 2023

Once you decided how to move forward please let me know what I need to change to make it work.

@shreve
Copy link
Contributor

shreve commented Mar 6, 2023

I'm still confused about the value of being able to set allow-errors=False

I think from an end-user perspective it makes sense. If you have an assignment where each test must pass before the next one should be attempted, why waste the resources running cells that you know will fail?

Do you have more detail about what systems/functionality will fail in that scenario? I'm not incredibly familiar with the whole codebase yet.

@brichet
Copy link
Contributor

brichet commented Mar 7, 2023

Do you have more detail about what systems/functionality will fail in that scenario?

This would break the rendering of the validation dialog.
The validation process catches all errors and displays them with a specific formatting, which is lost if the error is raised by nbclient.
Here are the 2 renderings, the one on the left is an extract of the current and the one on the right with allow_errors=False.

I think it deserves more discussion since it breaks a part of the rendering of nbgrader.
Maybe you should open an issue if this feature is really necessary.

shreve added a commit to shreve/nbgrader that referenced this pull request Mar 7, 2023
Update the traits on the Execute preprocessor based on the discussion in
jupyter#1690, including adding a
version pin of ipython >= 8.10.0
shreve added a commit to shreve/nbgrader that referenced this pull request Mar 7, 2023
Update the traits on the Execute preprocessor based on the discussion in
jupyter#1690, including adding a
version pin of ipython >= 8.10.0

Also, reorder traits to match the upstream parent, NotebookClient.
shreve added a commit to shreve/nbgrader that referenced this pull request Mar 7, 2023
Update the traits on the Execute preprocessor based on the discussion in
jupyter#1690, including adding a
version pin of ipython >= 8.10.0

Also, reorder traits to match the upstream parent, NotebookClient.
@shreve
Copy link
Contributor

shreve commented Mar 7, 2023

I think it deserves more discussion since it breaks a part of the rendering of nbgrader.
Maybe you should open an issue if this feature is really necessary.

@brichet I agree. The change to allow_errors is more influential than I thought. This would be great before a 1.0, but deserves it's own ticket.

@tmetzl I made a PR against yours with the changes I think we need here.

I was expecting to be able to remove the timeout and interrupt_on_timeout traits, but they need to be included because their default values are different from the parent default values. 🙃

@shreve
Copy link
Contributor

shreve commented Mar 9, 2023

Oof, ok. iPython dropped support for python 3.7 a little over a year ago, so setting the minimum version broke nbgrader's compatibility.

ipython/ipython@5b9aa9c

In that commit, they cite NEP 29 — Recommend Python and NumPy version support as a community policy standard which suggests a time table for dropping support for older pythons which NumPy recommends to the rest of the community as well.

In order to merge this change, we now need to figure out a strategy for dealing with python 3.7 and implement it. I would personally be in favor of upping the minimum from 3.7 to 3.8, but we could pick another way forward. @brichet what would your preference be? Does a decision like this need wider approval?

@brichet
Copy link
Contributor

brichet commented Mar 10, 2023

Thanks @shreve for changes and @tmetzl for merging it.

@brichet what would your preference be?

I don't have a strong opinion on dropping unmaintained Python version.
I opened #1750 to drop Python 3.7 in tests and include Python 3.11 instead. Let's wait for it to rebase the current branch.

I was expecting to be able to remove the timeout

I think it can be removed, since it was introduced to avoid infinite loop in case of a cheat attempt using showTraceback() (fixed in ipython/ipython#13934).
This can also be done in a follow up PR.

@shreve
Copy link
Contributor

shreve commented Mar 13, 2023

Great! I see that change was merged. I think that means with a rebase off main, this PR should be good to go!

@shreve
Copy link
Contributor

shreve commented Mar 23, 2023

@tmetzl Are you able to do this update? Either rebase off main or merge main in. It would be awesome to get this merged in time for 0.8.2.

tmetzl and others added 3 commits March 23, 2023 16:55
Update the traits on the Execute preprocessor based on the discussion in
jupyter#1690, including adding a
version pin of ipython >= 8.10.0

Also, reorder traits to match the upstream parent, NotebookClient.
@tmetzl
Copy link
Contributor Author

tmetzl commented Mar 23, 2023

@tmetzl Are you able to do this update? Either rebase off main or merge main in. It would be awesome to get this merged in time for 0.8.2.

@shreve I think I did now. Please let me know if there is anything left to do.

@brichet
Copy link
Contributor

brichet commented Mar 23, 2023

Thanks @tmetzl and @shreve

@brichet brichet merged commit 0a7c117 into jupyter:main Mar 23, 2023
25 checks passed
@shreve
Copy link
Contributor

shreve commented Mar 23, 2023

Beautiful! Thanks all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants