9

I'm developing a backup daemon that will run silently in the background. The daemon relies on the duplicity backup software, which when backing up requires an encryption key. I cannot ask for the password through the console because obviously, the daemon has no access to such.

How could I easily create a prompt that asks the user to type in a password, and returns it to the application (through a Python variable)? I'm using Python 2.7.

chrisaycock
  • 36,470
  • 14
  • 88
  • 125
liamzebedee
  • 14,010
  • 21
  • 72
  • 118

5 Answers5

35

Because you asked for the simplest (Python 2.7):

import Tkinter, tkSimpleDialog
tkSimpleDialog.askstring("Password", "Enter password:", show='*')

For Python 3.3:

import tkinter
tkinter.simpledialog.askstring("Password", "Enter password:", show='*')

For Python 3.6+:

import tkinter as tk
import tkinter.simpledialog
tk.Tk().withdraw()
tkinter.simpledialog.askstring("Password", "Enter password:", show='*')
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • 1
    For Python 3.3: import tkinter tkinter.simpledialog.askstring("Password", "Enter password:", show='*') – ryry1985 Feb 06 '17 at 18:14
  • 4
    Seems only to work, if you initialize Tk() first, without it fails with `'NoneType' object has no attribute 'winfo_viewable'`. At least in Python 3.6 – Andreas H. Aug 14 '18 at 14:25
5
from Tkinter import *

def getpwd():
    password = ''
    root = Tk()
    pwdbox = Entry(root, show = '*')
    def onpwdentry(evt):
         password = pwdbox.get()
         root.destroy()
    def onokclick():
         password = pwdbox.get()
         root.destroy()
    Label(root, text = 'Password').pack(side = 'top')

    pwdbox.pack(side = 'top')
    pwdbox.bind('<Return>', onpwdentry)
    Button(root, command=onokclick, text = 'OK').pack(side = 'top')

    root.mainloop()
    return password
pycoder112358
  • 875
  • 5
  • 10
  • Doesn't work for me. I fixed an issue with root being referenced before defined. Now I can get the dialog to show, but when I type the password 1) **the characters show, instead of black dots** and 2) when I hit return **nothing is returned from `getpwd`**. Also, I get an error when I click OK - onpwdentry() takes exactly 1 argument Thanks for the quick response. – liamzebedee Mar 30 '13 at 23:55
  • Ok, that last edit should fix your problems. For 1) I set the "show" property of the Entry widget to '*', so it'll show asterisks instead of letters. 2) was an incredibly stupid error on my part: I had the subfunction returning the password, when I obviously wanted the main function returning it. mainloop() is blocking, so the password won't be returned until the window is destroyed by onokclick() or onpwdentry() – pycoder112358 Mar 31 '13 at 00:12
  • Thanks, just fixed the last bug, which was modifying the password outside its scope, which meant that the method was still returning "". Added a nonlocal hack for Python 2.x (using dict), and it successfully works! – liamzebedee Mar 31 '13 at 00:16
  • Glad to help! Sorry about all the errors the first two edits; I don't seem to be thinking too well tonight. – pycoder112358 Mar 31 '13 at 00:18
  • @pycoder112358 still scoping issue are there i have updated with procedure at the bottom – Mallikarjunarao Kosuri Mar 08 '16 at 04:38
5

Because not everyone wants to use TK, here's a script using PyQt:

from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit
import sys
app = QApplication(sys.argv)
qd = QInputDialog()
qd.setTextEchoMode(QLineEdit.Password)
qd.show()
app.exec()

And, because you wouldn't usually just ask a user for a password just for the heck of it:

#!/bin/env python3
#passwordPrompt.py

from PyQt5.QtWidgets import QApplication, QInputDialog
import sys, time

def succFunc():
  sys.stdout.write(qd.textValue())
  sys.stdout.flush()
  exit(0)

def failFunc():
  exit(1)

app = QApplication(sys.argv)
qd = QInputDialog()
#QLineEdit.Password
qd.setTextEchoMode(2)
qd.rejected.connect(failFunc)
qd.accepted.connect(succFunc)
qd.show()
app.exec()

And the corresponding bash function:

#!/bin/bash

passwordPrompt.py | tee
smaudet
  • 609
  • 8
  • 17
3

Resolved scoping issues of @pycoder112358 post:

from tkinter import *

PASSWORD = ''

def get_passwd():
    global PASSWORD
    root = Tk()
    pwdbox = Entry(root, show = '*')

    def onpwdentry(evt):
        global PASSWORD 
        PASSWORD = pwdbox.get()
        root.destroy()
    def onokclick():     
        global PASSWORD
        PASSWORD = pwdbox.get()
        root.destroy()

    Label(root, text = 'Password').pack(side = 'top')

    pwdbox.pack(side = 'top')
    pwdbox.bind('<Return>', onpwdentry)
    Button(root, command=onokclick, text = 'OK').pack(side = 'top')

    root.mainloop()
    return PASSWORD
Adam Katz
  • 14,455
  • 5
  • 68
  • 83
Mallikarjunarao Kosuri
  • 1,023
  • 7
  • 25
  • 52
3

Expanding on Diego's answer with some minimal housekeeping (without this I was getting crashes galore trying to use his beautifully brief example):

import Tkinter, tkSimpleDialog 
root = Tkinter.Tk() # dialog needs a root window, or will create an "ugly" one for you
root.withdraw() # hide the root window
password = tkSimpleDialog.askstring("Password", "Enter password:", show='*', parent=root)
root.destroy() # clean up after yourself!

This will work well from a program that is otherwise just a terminal / console application.

dsz
  • 4,542
  • 39
  • 35
  • For me this is the best solution since I'm having a console application. But how to open the dialog window on the main screen, when having multiple screens on Linux? The solution from @pycoder112358 opens the dialog on the main screen. – eztam Nov 11 '16 at 11:43