I have python script (3rd party script) calling getpass.
I need to use a script to provide the password. Simple piping doesn't work because getpass, according to the doc, reads from /dev/tty.
I'm by no means a shell or python expert, so is there a way (without modifying the .py script that uses getpass) to provide the password via bash\shell script?
Asked
Active
Viewed 1,109 times
5

sel
- 483
- 5
- 16
2 Answers
4
This is basically designed to get input from the keyboard. If you don't want that, and you can't change the script to read it from a correctly-permissioned configuration file instead, then I would just monkeypatch out the getpass call entirely.
from mock import patch
with patch('path.to.import.getpass') as mock:
mock.return_value = 'hunter2'
# call your script
To elaborate on "call your script", if this script has a proper entry point, e.g. generated from setuptools, then look in the source to find the underlying function that is called. You may import the module and call the same function directly to "run" the script. However, if the script is just a bunch of module level code, then you'll have to use exec
and explicitly pass in a scope including the monkeypatch.

wim
- 338,267
- 99
- 616
- 750
-
1wim, how is this different from using a hardcoded/plaintext pass directly? (as thats the only thing OP is dealing with as far as `getpass` is concerned). – heemayl Feb 26 '18 at 19:35
-
This is a good answer, but it may be nice to include some information on how one might actually do the *call your script* part. This may dependon specifics of that script, but perhaps reading the source script and using `exec` could be a catch-all. – sytech Feb 26 '18 at 19:36
-
As I understood it, the OP is calling a script which calls `getpass.getpass()` and they can't modify the script. – wim Feb 26 '18 at 19:37
-
@heemayl to my understanding the goal was to circumvent the fact that `getpass` asks for input (IE blocks and is not suitable for scriptable use) -- without modifying the source that calls `getpass`. The solution @wim gave is just a minimal example. One could get the `return_value` for the mocked getpass by other means than hard-coding if they want. I don't think hard-coded/plaintext was necessarily part of the issue here, but maybe OP can clarify. – sytech Feb 26 '18 at 19:38
-
1Yes `'hunter2'` can come from another script or a permissioned file if necessary. My point here is not to try and defeat `getpass` (difficult), but to just patch it out in the first place (easy). – wim Feb 26 '18 at 19:44
-
Good idea! However it causes get pass to return an object of type MagicMock instead of string (as in your example). – sel Feb 27 '18 at 08:40
-
@sel Fixed, I think. The specifics of the patch kinda depends whether it's imported like `getpass` and used `getpass.getpass()`, or imported like `from getpass import getpass`. Confusing. – wim Feb 27 '18 at 16:46
-
Another way I found without mocking is to override getpass.getpass, and then call the 3rd party script with import. – sel Feb 27 '18 at 16:50
-
Yes, but be careful with that. `import` will only execute the code at module level scope on the first occasion. Subsequent imports within the same session will just re-use the existing module object from `sys.modules`. – wim Feb 27 '18 at 16:52
0
If you cannot rely on mock
for whatever reason, you can use a simple context manager:
class NeuteredGetpass:
def __init__(self, new_pwd):
def noop(*args, **kwargs):
return new_pwd
self.oldpass = getpass.getpass
self.newpass = noop
def __enter__(self):
getpass.getpass = self.newpass
def __exit__(self, exc_type, exc_val, exc_tb):
getpass.getpass = self.oldpass
use_this_pass = NeuteredGetpass
with use_this_pass('password_from_shell_script'):
# do your stuff
...

Giacomo Lacava
- 1,784
- 13
- 25