PyGame and Pyglet both support joysticks but their joystick stuff is integrated with the event systems for those APIs.
Threading in Python is easy but still more work than not. It all worked perfectly in the end but was not especially quick and easy to write. That was for an in-house project at Aeon Imaging and I left that code there so I don’t have anything to share here. I think for the other joystick I just polled it on each pass through the main 60Hz drawing loop, the input is buffered so you don’t lose anything even if the button press duration could be less than 1/60th of a second or whatever is you animation loop period. For the one which blocked I created a python thread which blocked on input and wrote to a thread-safe structure which the main Python thread checked. Reading from one of those joysticks blocked until there was input and for the other reading always returned immediately. The documentation was lacking for that Python HID binding so I read the underling C API which it called and also experiment with some of the Python calls to learn how it worked. It should work for any USB joystick, they are all HID devices. I did that on Linux a couple of months ago for one of these arcade joysticks and an old Gravis Gamepad Pro.
You could try a Python binding for one of the cross-platform HID APIs.