How to override getpass function in a jupyter notebook for testing?

Viewed 176

I'm trying to test a jupyter notebook with nbval.

py.test --nbval ~/MyNotebook.ipynb 

Howver, the notebook has a getpass() function in it. This blocks the test:

========================================================================================= FAILURES =========================================================================================
__________________________________________________________________________________ Untitled.ipynb::Cell 1 __________________________________________________________________________________
Notebook cell execution failed
Cell 1: Cell execution caused an exception

Input:
getpass.getpass()

Traceback:

---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
<ipython-input-2-b4e90adc512e> in <module>
----> 1 getpass.getpass()

/opt/miniconda/lib/python3.8/site-packages/ipykernel/kernelbase.py in getpass(self, prompt, stream)
    834         """
    835         if not self._allow_stdin:
--> 836             raise StdinNotImplementedError(
    837                 "getpass was called, but this frontend does not support input requests."
    838             )

StdinNotImplementedError: getpass was called, but this frontend does not support input requests.

I've tried to override the function, however it still prompts for input:

enter image description here

How can I override the getpass() function or provide Stdin so that it doesn't block for user input?

1 Answers

What you tried would seem work, here it is in the Python interpreter

>>> import getpass
>>> getpass.getpass = lambda: "abc"
>>> getpass.getpass()
'abc'

However, on Jupyter, IPython overrides getpass between blocks. The solution is either to monkeypatch at the start of every block, or monkeypatch IPython directly:

The IPython version of getpass is defined here and here, I hacked accordingly.

import IPython
from ipykernel.kernelapp import IPythonKernel
import builtins

# Modified version of original function
def _forward_input_hacked(self, allow_stdin=False):
    """Forward raw_input and getpass to the current frontend.
    via input_request (hacked)
    """
    self._allow_stdin = allow_stdin

    self._sys_raw_input = builtins.input
    builtins.input = self.raw_input

    self._save_getpass = getpass.getpass
    getpass.getpass = lambda: "xyz"
    #getpass.getpass = self.getpass

ipyk = IPython.Application.instance().kernel
ipyk._forward_input = _forward_input_hacked.__get__(ipyk, IPythonKernel)
import getpass

This should persist across blocks.

Note: a previous version of my answer did not answer the question properly, this monkeypatch should do the trick.

Related