[issue11466] getpass.getpass doesn't close tty file

Steffen Daode Nurpmeso report at bugs.python.org
Sat Mar 19 23:40:58 CET 2011


Steffen Daode Nurpmeso <sdaoden at googlemail.com> added the comment:

On Sat, Mar 19, 2011 at 01:29:28PM +0000, Éric Araujo wrote:
> It’s a private function, if that makes the patch smaller let’s change it.

The promised 11466.5.patch.  It:

    - Fixes #11466 resource warning.
    - Fixes bogus newline which would have been written before 
      in case the fallback implementation needs to be used.
      + _raw_input() has been renamed to _user_input() because that 
        is what it actually does (wether it's raw depends on caller). 
        It will now encapsulate the complete user prompting, 
        thus including the mentioned final newline.
    - Allows patch-in of #11236 patch without any further 
      adjustments (i.e. no additional catch of another resource 
      warning necessary).
    - It cleans up the control flow and the comments a bit which 
      i think was the reason that the first two items above could 
      actually become introduced in the code at all.

But - it's even larger than 11466.4.patch!

----------
Added file: http://bugs.python.org/file21297/11466.5.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue11466>
_______________________________________
-------------- next part --------------
diff --git a/Lib/getpass.py b/Lib/getpass.py
--- a/Lib/getpass.py
+++ b/Lib/getpass.py
@@ -38,27 +38,25 @@
 
     Always restores terminal settings before returning.
     """
-    fd = None
-    tty = None
-    try:
-        # Always try reading and writing directly on the tty first.
-        fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
-        tty = os.fdopen(fd, 'w+', 1)
-        input = tty
-        if not stream:
-            stream = tty
-    except EnvironmentError as e:
-        # If that fails, see if stdin can be controlled.
+    tty, exinst, passwd = None, None, None
+    # Something to break off if an error happens
+    while 1:
         try:
-            fd = sys.stdin.fileno()
-        except (AttributeError, ValueError):
-            passwd = fallback_getpass(prompt, stream)
-        input = sys.stdin
-        if not stream:
-            stream = sys.stderr
+            # Always try reading and writing directly on the tty first.
+            fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
+            input = tty = os.fdopen(fd, 'w+', 1)
+            if not stream:
+                stream = tty
+        except EnvironmentError:
+            # If that fails, see if stdin can be controlled;
+            # use generic fallback implementation as last resort
+            try:
+                fd = sys.stdin.fileno()
+            except:
+                break
+            if not stream:
+                stream = sys.stderr
 
-    if fd is not None:
-        passwd = None
         try:
             old = termios.tcgetattr(fd)     # a copy to save
             new = old[:]
@@ -68,21 +66,29 @@
                 tcsetattr_flags |= termios.TCSASOFT
             try:
                 termios.tcsetattr(fd, tcsetattr_flags, new)
-                passwd = _raw_input(prompt, stream, input=input)
+                passwd = _user_input(prompt, stream, input=input, echooff=True)
+            except Exception as e:
+                exinst = e
             finally:
                 termios.tcsetattr(fd, tcsetattr_flags, old)
-                stream.flush()  # issue7208
-        except termios.error as e:
+                stream.flush() # issue7208 (7246)
+        except Exception as e:
             if passwd is not None:
-                # _raw_input succeeded.  The final tcsetattr failed.  Reraise
-                # instead of leaving the terminal in an unknown state.
-                raise
-            # We can't control the tty or stdin.  Give up and use normal IO.
-            # fallback_getpass() raises an appropriate warning.
-            del input, tty  # clean up unused file objects before blocking
-            passwd = fallback_getpass(prompt, stream)
+                # _user_input succeeded, but the final tcsetattr failed.
+                # Reraise the termios.error instead of leaving the terminal
+                # in an unknown state.
+                exinst = e
+        break
 
-    stream.write('\n')
+    if not exinst and passwd is None:
+        # We can't control the tty or stdin. Give up and use normal IO.
+        # fallback_getpass() raises an appropriate warning.
+        passwd = fallback_getpass(prompt, stream)
+
+    if tty:
+        tty.close()
+    if exinst:
+        raise exinst
     return passwd
 
 
@@ -115,10 +121,10 @@
     if not stream:
         stream = sys.stderr
     print("Warning: Password input may be echoed.", file=stream)
-    return _raw_input(prompt, stream)
+    return _user_input(prompt, stream, input=None, echooff=False)
 
 
-def _raw_input(prompt="", stream=None, input=None):
+def _user_input(prompt, stream, input=None, echooff=False):
     # This doesn't save the string in the GNU readline history.
     if not stream:
         stream = sys.stderr
@@ -130,6 +136,8 @@
         stream.flush()
     # NOTE: The Python C API calls flockfile() (and unlock) during readline.
     line = input.readline()
+    if echooff:
+        stream.write('\n')
     if not line:
         raise EOFError
     if line[-1] == '\n':


More information about the Python-bugs-list mailing list