The Solution
The solution is easy. Just include this module as a hidden import in the PyInstaller program:
python -m PyInstaller your_program.py --onefile --hidden-import=pynput.keyboard._xorg
If you also use the mouse with pynput, then you'll get the same error with the module pynput.mouse._xorg
. So do this:
python -m PyInstaller your_program.py --onefile --hidden-import=pynput.keyboard._xorg --hidden-import=pynput.mouse._xorg
Warning! You'll likely get a different module that it doesn't find, if you're packaging for Windows or Mac. This is what you get for Linux. If you want your program to be cross-platform, then you'll have to package the program, for example, for Windows and test it to see which module it doesn't find and include it as a hidden import.
For example, if you want your program to work on Linux and Windows, use this command:
python -m PyInstaller your_program.py --onefile --hidden-import=pynput.keyboard._xorg --hidden-import=pynput.mouse._xorg --hidden-import=pynput.keyboard._win32 --hidden-import=pynput.mouse._win32
If you have a lot of hidden modules, then you may edit the .spec file and add the modules to the hiddenimports
list like so (on PyInstaller 4.1):
hiddenimports=['pynput.keyboard._xorg', 'pynput.mouse._xorg'],
Why The Error
When you see an ImportError
in a Python program packaged by PyInstaller, there is a high chance that the problem is that PyInstaller couldn't detect that specific import and didn't include it in the binary, hence the import error.
In the error message it tells you which module it didn't find:
ImportError: this platform is not supported: No module named 'pynput.keyboard._xorg'
which is pynput.keyboard._xorg
, because you're on Linux.
It couldn't find the module, because it was imported in a "non-traditional" way. Look at the source code for pynput/_util/__init__.py
in the backend
function:
def backend(package):
backend_name = os.environ.get(
'PYNPUT_BACKEND_{}'.format(package.rsplit('.')[-1].upper()),
os.environ.get('PYNPUT_BACKEND', None))
if backend_name:
modules = [backend_name]
elif sys.platform == 'darwin':
modules = ['darwin']
elif sys.platform == 'win32':
modules = ['win32']
else:
modules = ['xorg']
errors = []
resolutions = []
for module in modules:
try:
return importlib.import_module('._' + module, package)
except ImportError as e:
errors.append(e)
if module in RESOLUTIONS:
resolutions.append(RESOLUTIONS[module])
raise ImportError('this platform is not supported: {}'.format(
'; '.join(str(e) for e in errors)) + ('\n\n'
'Try one of the following resolutions:\n\n'
+ '\n\n'.join(
' * {}'.format(s)
for s in resolutions))
if resolutions else '')
You can see that it uses the import_module
function from the importlib
module to import the correct module for the platform. This is why it couldn't find the
pynput.keyboard._xorg
module.
Your Second Question
Also is there anyway to use Pynput on a system without a gui?
I don't know.