Hello community, here is the log from the commit of package python-system_hotkey for openSUSE:Factory checked in at 2020-10-23 12:22:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-system_hotkey (Old) and /work/SRC/openSUSE:Factory/.python-system_hotkey.new.3463 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-system_hotkey" Fri Oct 23 12:22:25 2020 rev:2 rq:843483 version:1.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-system_hotkey/python-system_hotkey.changes 2020-05-26 17:21:57.700315511 +0200 +++ /work/SRC/openSUSE:Factory/.python-system_hotkey.new.3463/python-system_hotkey.changes 2020-10-23 12:23:55.940750595 +0200 @@ -1,0 +2,8 @@ +Thu Oct 22 16:48:44 UTC 2020 - andy great <andythe_gr...@pm.me> + +- Update to version 1.0.3. + * Fix a typo in Readme + * Documented the fact that xlib shouldn't really be used +- Cleanup spec file. + +------------------------------------------------------------------- Old: ---- system_hotkey-1.0.2.tar.gz New: ---- system_hotkey-1.0.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-system_hotkey.spec ++++++ --- /var/tmp/diff_new_pack.3mL1cI/_old 2020-10-23 12:23:57.776755861 +0200 +++ /var/tmp/diff_new_pack.3mL1cI/_new 2020-10-23 12:23:57.780755872 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-system_hotkey -Version: 1.0.2 +Version: 1.0.3 Release: 0 Summary: System wide hotkeys License: BSD-3-Clause @@ -44,8 +44,6 @@ %prep %setup -q -n system_hotkey-%{version} -# version was not bumped -sed -i -e 's:1\.0\.1:1.0.2:g' setup.py %build %python_build @@ -61,10 +59,7 @@ %files %{python_files} %license LICENSE %doc HISTORY.rst README.rst -%dir %{python_sitelib}/system_hotkey/ -%dir %{python_sitelib}/system_hotkey/__pycache__/ -%{python_sitelib}/system_hotkey-%{version}-py%{python_version}.egg-info/ -%{python_sitelib}/system_hotkey/*.py -%pycache_only %{python_sitelib}/system_hotkey/__pycache__/*.pyc +%{python_sitelib}/system_hotkey +%{python_sitelib}/*egg-info %changelog ++++++ system_hotkey-1.0.2.tar.gz -> system_hotkey-1.0.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/.gitignore new/system_hotkey-1.0.3/.gitignore --- old/system_hotkey-1.0.2/.gitignore 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/.gitignore 2019-05-15 14:53:03.000000000 +0200 @@ -6,5 +6,5 @@ *egg-info/ *.bat .cache/ - +tags diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/HISTORY.rst new/system_hotkey-1.0.3/HISTORY.rst --- old/system_hotkey-1.0.2/HISTORY.rst 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/HISTORY.rst 2019-05-15 14:53:03.000000000 +0200 @@ -4,11 +4,22 @@ Mac support -eta > 9 months +eta > 8 months Version Release Notes ===================== +1.0.4 +----- +* Exceptions are now run in main + +1.0.3 +----- +* Documented the fact that xlib shouldn't really be used + +1.0.2 +----- +* Fixed a linux bug where spurious events got passed through 1.0.0 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/README.rst new/system_hotkey-1.0.3/README.rst --- old/system_hotkey-1.0.2/README.rst 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/README.rst 2019-05-15 14:53:03.000000000 +0200 @@ -5,8 +5,6 @@ Currently no mac or python2 support :( -Mac support is coming in a few years i would say! - Installation ------------ @@ -15,7 +13,7 @@ .. code-block:: bash - pip3 install system_hotkey + pip3 install system_hotkey should do the trick @@ -25,9 +23,9 @@ Linux ^^^^^ -For x11 you will can either use `xcffib <https://github.com/tych0/xcffib>`_ (bsd license), -or you may use the python xlib bindings (gpl license) +For x11 you should use `xcffib <https://github.com/tych0/xcffib>`_ (bsd license), +If for some reason you have to use the python xlib bindings (gpl license), a few fixes need be added first. See `here <https://github.com/timeyyy/system_hotkey/issues/6#issuecomment-265410255>`_ Usage @@ -46,16 +44,16 @@ - control - shift -- super (win key) +- super (windows key) - alt -A InvalidKeyError will be raised if a key was not understood +InvalidKeyError will be raised if a key was not understood .. code-block:: python from system_hotkey import SystemHotkey - hk = SystemHotkeys() - hk.register(('control', 'shift', 'h'), callback=lambda:print("Easy!")) + hk = SystemHotkey() + hk.register(('control', 'shift', 'h'), callback=lambda x: print("Easy!")) A SystemRegisterError will be raised if a hotkey is already in use. @@ -76,7 +74,7 @@ def some_func(self, event, hotkey, args): pass - hk = SystemHotkeys(consumer=some_func) + hk = SystemHotkey(consumer=some_func) hk.register(hotkey, arg1, arg2, arg3) So you have a master function that receives all hotkey presses and can delegate as desired. @@ -93,3 +91,5 @@ - I have only mapped most common keys, i have not experimented with Unicode/Japanese characters etc. It's only a matter of mapping a name to the keysym on Linux and virtual key code on windows. - binding to kp_left (key pad left) will also bind to kp_4, there is a flag (unite_kp) to toggle this behaviour but it is experimental + +- Requires an xserver (x11)... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/setup.py new/system_hotkey-1.0.3/setup.py --- old/system_hotkey-1.0.2/setup.py 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/setup.py 2019-05-15 14:53:03.000000000 +0200 @@ -1,5 +1,5 @@ from setuptools import setup, find_packages -from codecs import open +from codecs import open import os here = os.path.abspath(os.path.dirname(__file__)) @@ -8,88 +8,32 @@ with open(os.path.join(*paths), 'r') as f: return f.read() -#if os.name == 'nt': -# REQUIRED = ['pywin32'] -#else: -REQUIRED = [] - setup( name = 'system_hotkey', - - version='1.0.1', - + version='1.0.3', description = 'System wide hotkeys', long_description = (read('README.rst') + '\n\n' + read('HISTORY.rst') + '\n\n' + read('AUTHORS.rst')), - url = 'https://github.com/timeyyy/system_hotkey', - author='timothy eichler', author_email='tim_eich...@hotmail.com', - license='BSD3', - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ # How mature is this project? Common values are #~ 3 - Alpha # 4 - Beta # 5 - Production/Stable - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'License :: OSI Approved :: BSD License', - # Specify the Python versions you support here. In particular, ensure - # that you indicate whether you support Python 2, Python 3 or both. #~ 'Programming Language :: Python :: 2', - #~ 'Programming Language :: Python :: 2.6', - #~ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', ], # What does your project relate to? keywords = 'hotkeys python3 shortcutkeys shortuct x11 windows', - - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). packages=find_packages(exclude=['contrib', 'docs', 'tests*']), - - # List run-time dependencies here. These will be installed by pip when your - # project is installed. For an analysis of "install_requires" vs pip's - # requirements files see: - # https://packaging.python.org/en/latest/requirements.html - install_requires = REQUIRED, - - # List additional groups of dependencies here (e.g. development dependencies). - # You can install these using the following syntax, for example: - # $ pip install -e .[dev,test] - #~ extras_require = { - #~ 'dev': ['check-manifest'], - #~ 'test': ['coverage'], - #~ }, - - # If there are data files included in your packages that need to be - # installed, specify them here. If using Python 2.6 or less, then these - # have to be included in MANIFEST.in as well. - #~ package_data={ - #~ 'sample': ['package_data.dat'], - #~ }, - - # Although 'package_data' is the preferred approach, in some case you may - # need to place data files outside of your packages. - # see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files - # In this case, 'data_file' will be installed into '<sys.prefix>/my_data' - #~ data_files=[('my_data', ['data/data_file'])], - - # To provide executable scripts, use entry points in preference to the - # "scripts" keyword. Entry points provide cross-platform support and allow - # pip to create the appropriate form of executable for the target platform. - #~ entry_points = { - #~ 'console_scripts': [ - #~ 'sample=sample:main',],}, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/system_hotkey/system_hotkey.py new/system_hotkey-1.0.3/system_hotkey/system_hotkey.py --- old/system_hotkey-1.0.2/system_hotkey/system_hotkey.py 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/system_hotkey/system_hotkey.py 2019-05-15 14:53:03.000000000 +0200 @@ -137,16 +137,18 @@ # win32con.NUMLOCK_ON, # win32con.NUMLOCK_ON | win32con.CAPSLOCK_ON ) +# Linux else: try: from . import xpybutil_keybind as keybind except SystemError: import xpybutil_keybind as keybind + # Xcb try: - import xcffib # the xproto con will not work unlesss you inport this first + # Xproto con will not work unlesss you inport this first + import xcffib from xcffib import xproto - from xpybutil import keybind xcb_modifiers = { 'control' : xproto.ModMask.Control, @@ -161,105 +163,69 @@ xproto.ModMask.Lock | xproto.ModMask._2) except ImportError: pass - try: - from Xlib import X - from Xlib import XK - from Xlib.display import Display - special_X_keysyms = { # these couldn't be gotten from the xlib keycode function - ' ' : "space", - '\t' : "Tab", - '\n' : "Return", - '\r' : "Return", - '\e' : "Escape", - '!' : "exclam", - '#' : "numbersign", - '%' : "percent", - '$' : "dollar", - '&' : "ampersand", - '"' : "quotedbl", - '\'' : "apostrophe", - '(' : "parenleft", - ')' : "parenright", - '*' : "asterisk", - '=' : "equal", - '+' : "plus", - ',' : "comma", - '-' : "minus", - '.' : "period", - '/' : "slash", - ':' : "colon", - ';' : "semicolon", - '<' : "less", - '>' : "greater", - '?' : "question", - '@' : "at", - '[' : "bracketleft", - ']' : "bracketright", - '\\' : "backslash", - '^' : "asciicircum", - '_' : "underscore", - '`' : "grave", - '{' : "braceleft", - '|' : "bar", - '}' : "braceright", - '~' : "asciitilde" - } - - xlib_modifiers = { - 'control' : X.ControlMask, - 'shift' : X.ShiftMask, - 'alt' : X.Mod1Mask, - 'super' : X.Mod4Mask - } - - xlib_trivial_mods = ( # only working for scrollock - 0, - X.LockMask, - X.Mod2Mask, - X.LockMask | X.Mod2Mask) - except ImportError: - pass + # Xlib + else: + try: + from Xlib import X + from Xlib import XK + from Xlib.display import Display + # These couldn't be gotten from the xlib keycode function + special_X_keysyms = { + ' ' : "space", + '\t' : "tab", + '\n' : "return", + '\r' : "return", + '\e' : "escape", + '!' : "exclam", + '#' : "numbersign", + '%' : "percent", + '$' : "dollar", + '&' : "ampersand", + '"' : "quotedbl", + '\'' : "apostrophe", + '(' : "parenleft", + ')' : "parenright", + '*' : "asterisk", + '=' : "equal", + '+' : "plus", + ',' : "comma", + '-' : "minus", + '.' : "period", + '/' : "slash", + ':' : "colon", + ';' : "semicolon", + '<' : "less", + '>' : "greater", + '?' : "question", + '@' : "at", + '[' : "bracketleft", + ']' : "bracketright", + '\\' : "backslash", + '^' : "asciicircum", + '_' : "underscore", + '`' : "grave", + '{' : "braceleft", + '|' : "bar", + '}' : "braceright", + '~' : "asciitilde" + } + + xlib_modifiers = { + 'control' : X.ControlMask, + 'shift' : X.ShiftMask, + 'alt' : X.Mod1Mask, + 'super' : X.Mod4Mask + } + + xlib_trivial_mods = ( # only working for scrollock + 0, + X.LockMask, + X.Mod2Mask, + X.LockMask | X.Mod2Mask) + except ImportError: + pass -special_X_keysyms = { - ' ' : "space", - '\t' : "Tab", - '\n' : "Return", - '\r' : "Return", - '\e' : "Escape", - '!' : "exclam", - '#' : "numbersign", - '%' : "percent", - '$' : "dollar", - '&' : "ampersand", - '"' : "quotedbl", - '\'' : "apostrophe", - '(' : "parenleft", - ')' : "parenright", - '*' : "asterisk", - '=' : "equal", - '+' : "plus", - ',' : "comma", - '-' : "minus", - '.' : "period", - '/' : "slash", - ':' : "colon", - ';' : "semicolon", - '<' : "less", - '>' : "greater", - '?' : "question", - '@' : "at", - '[' : "bracketleft", - ']' : "bracketright", - '\\' : "backslash", - '^' : "asciicircum", - '_' : "underscore", - '`' : "grave", - '{' : "braceleft", - '|' : "bar", - '}' : "braceright", - '~' : "asciitilde" - } class Aliases(): ''' @@ -291,7 +257,7 @@ thread_safe = util.CallSerializer() class MixIn(): - @thread_safe.serialize_call + @thread_safe.serialize_call(0.5) def register(self, hotkey, *args, callback=None, overwrite=False): ''' Add a system wide hotkey, @@ -328,7 +294,6 @@ else: msg = 'existing bind detected... unregister or set overwrite to True' raise SystemRegisterError(msg, *hotkey) - return if os.name == 'nt': def nt_register(): @@ -367,11 +332,14 @@ #~ copy[-1] = 'keyrelease' #~ self.keybinds[tuple(copy)].append(callback) - @thread_safe.serialize_call + @thread_safe.serialize_call(0.5) def unregister(self, hotkey): ''' Remove the System wide hotkey, the order of the modifier keys is irrelevant + + InvalidKeyError raised if key not understood + UnregisterError raiesd if key doesn't exist ''' keycode, masks = self.parse_hotkeylist(hotkey) if os.name == 'nt': @@ -392,9 +360,12 @@ try: for mod in self.trivial_mods: self.conn.core.UngrabKeyChecked(keycode, self.root, masks | mod).check() - except xproto.BadAccess: - raise UnregisterError("Failed unregs") - del self.keybinds[tuple(self.order_hotkey(hotkey))] + except xproto.BadAccess as e: + raise UnregisterError("Failed to unregister") from e + try: + del self.keybinds[tuple(self.order_hotkey(hotkey))] + except KeyError as err: + raise UnregisterError from err def order_hotkey(self, hotkey): # Order doesn't matter for modifiers, so we force an order here @@ -437,7 +408,7 @@ try: masks.append(self.modders[item]) except KeyError: - raise SystemRegisterError('Modifier: %s not supported' % item) #TBD rmeove how the keyerror gets displayed as well + raise SystemRegisterError('Modifier: %s not supported' % item) from None masks = self.or_modifiers_together(masks) else: masks = 0 @@ -463,7 +434,7 @@ if self.verbose: print('MFERROR', hotkey) # Possible numpad key? The keysym can change if shift is pressed (only tested on linux) - # Todo test numpad bindings on a non english system + # TODO test numpad bindings on a non english system aliases = NUMPAD_ALIASES.get(hotkey[-1]) if aliases: for key in aliases: @@ -532,7 +503,7 @@ ''' given a keycode returns a keysym ''' - # Todo + # TODO # --quirks-- # linux: # numpad keys with multiple keysyms are currently undistinguishable @@ -550,7 +521,7 @@ hk_ref = {} keybinds = {} - def __init__(self, consumer='callback', check_queue_interval=0.01, use_xlib=False, conn=None, verbose=False, unite_kp=True): + def __init__(self, consumer='callback', check_queue_interval=0.0001, use_xlib=False, conn=None, verbose=False, unite_kp=True): ''' if the consumer param = 'callback', -> All hotkeys will require a callback function @@ -672,7 +643,7 @@ if event.event_type == 'keypress': if self.verbose: print('calling ', repr(cb)) - cb(event) # TBD either throw these up in a thread, or pass in a queue to be put onto + cb(event) # TODO either throw these up in a thread, or pass in a queue to be put onto thread.start_new_thread(thread_me,(),) elif callable(consumer): @@ -773,7 +744,7 @@ return keybind.keysym_strings.get(keysym, [None])[0] def _xlib_the_grab(self, keycode, masks): - # Todo error handlig http://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html + # TODO error handlig http://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html # try: for triv_mod in self.trivial_mods: self.xRoot.grab_key(keycode, triv_mod | masks, 1, X.GrabModeAsync, X.GrabModeAsync) @@ -788,13 +759,13 @@ True, self.root, triv_mod | masks, keycode, xproto.GrabMode.Async, xproto.GrabMode.Async).check() - except struct.error: + except struct.error as e: msg = 'Unable to Register, Key not understood by systemhotkey' - raise InvalidKeyError(msg) - except xproto.AccessError: + raise InvalidKeyError(msg) from e + except xproto.AccessError as e: keysym = self._xcb_get_keysym(keycode) msg = 'The bind could be in use elsewhere: ' + keysym - raise SystemRegisterError(msg) + raise SystemRegisterError(msg) from e def _xcb_get_keycode(self, key): return keybind.lookup_string(key) @@ -806,7 +777,7 @@ if __name__ == '__main__': # hk = SystemHotkey(use_xlib=True, verbose=1, unite_kp=1) # hk = SystemHotkey(use_xlib=False, verbose=0) # xcb - #hk.register(('a',), callback=lambda e: print('hi')) + # hk.register(('a',), callback=lambda e: print('hi')) # hk.register(('kp_3',), callback=lambda e: print('hi')) #hk.register(('left',), callback=lambda e: print('hi')) @@ -825,20 +796,21 @@ # hk.register(('control','shift', 'k'), callback=lambda e: print('i am con shift k2')) def consumer(event, hotkey, args): if hotkey != ["f4"]: - import pdb;pdb.set_trace() + print(1) + # import pdb;pdb.set_trace() else: print(1) - hk = SystemHotkey(use_xlib=False, consumer=consumer, verbose=1, + hk = SystemHotkey(use_xlib=False, consumer=consumer, verbose=0, check_queue_interval=0.001) - hk.register(('f4',), callback=lambda e: print('hi')) + hk.register(('escape',), callback=lambda e: print('hi')) while 1: pass # Refernces # #https://wiki.python.org/moin/AppsWithPythonScripting -#~ http://msdn.microsoft.com/en-us/library/ms927178.aspx -#http://www.kbdedit.com/manual/low_level_vk_list.html -#http://stackoverflow.com/questions/14076207/simulating-a-key-press-event-in-python-2-7 #TBD WINDOWS KEY UP /DOWN ? +#vk codes http://msdn.microsoft.com/en-us/library/ms927178.aspx +#vk codes http://www.kbdedit.com/manual/low_level_vk_list.html +#http://stackoverflow.com/questions/14076207/simulating-a-key-press-event-in-python-2-7 #TODO WINDOWS KEY UP /DOWN ? #https://github.com/Tzbob/python-windows-tiler/blob/master/pwt/hotkey.py -# Tbd how to keep the lisetning alive if a n error is encounterd? how to recover? +#TODO how to keep the lisetning alive if an error is encounterd? how to recover? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/system_hotkey/tests/test_system_hotkey.py new/system_hotkey-1.0.3/system_hotkey/tests/test_system_hotkey.py --- old/system_hotkey-1.0.2/system_hotkey/tests/test_system_hotkey.py 1970-01-01 01:00:00.000000000 +0100 +++ new/system_hotkey-1.0.3/system_hotkey/tests/test_system_hotkey.py 2019-05-15 14:53:03.000000000 +0200 @@ -0,0 +1,44 @@ +from system_hotkey import SystemHotkey, \ + SystemRegisterError, \ + UnregisterError, \ + InvalidKeyError + +def test_errors_raised_in_main(): + hk = SystemHotkey() + key = ('5',) + cb = lambda e: print('hi') + + hk.register(key, callback=cb) + try: + hk.register(key, callback=cb) + except SystemRegisterError: + pass + else: + raise Exception('fail') + + hk.unregister(key) + try: + hk.unregister(key) + except UnregisterError: + pass + else: + raise Exception('fail') + + bad_key = ('badkey ..',) + try: + hk.register(bad_key, callback=cb) + except InvalidKeyError: + pass + else: + raise Exception('fail') + + try: + hk.unregister(bad_key) + except: + pass + else: + raise Exception('fail') + + # input() # Hold program open until you press enter + +test_errors_raised_in_main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/system_hotkey/util.py new/system_hotkey-1.0.3/system_hotkey/util.py --- old/system_hotkey-1.0.2/system_hotkey/util.py 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/system_hotkey/util.py 2019-05-15 14:53:03.000000000 +0200 @@ -4,8 +4,11 @@ general utilites.. ''' import _thread as thread +import threading from queue import Queue +import queue from functools import wraps +import time def unique_int(values): @@ -26,22 +29,93 @@ return last +class ExceptionSerializer(): + def __init__(self): + self.queue = queue.Queue() + + def catch_and_raise(self, func, timeout=0.5): + ''' + wait for a function to finish and raise any errors''' + self.wait_event(func, timeout) + self._check_for_errors(func) + + def mark_done(self, function): + '''Wrap functions so that we can monitor when they are done''' + self.init_wrap(function) + @wraps(function) + def decorator(*args, **kwargs): + # Function has started running + self.clear_event(function) + try: + results = function(*args, **kwargs) + except Exception as err: + self.queue.put(err) + else: + return results + finally: + # Function has finished running + self.set_event(function) + + return decorator + + def put(self, x): + self.queue.put(x) + + def init_wrap(self, func): + name = self._make_event_name(func) + event = threading.Event() + setattr(self, name, event) + + def _check_for_errors(self, func): + try: + error = self.queue.get(block=False) + except queue.Empty: + pass + else: + raise error + + def _make_event_name(self, func): + return '_event_' + func.__name__ + + def get_event(self, func): + return getattr(self, self._make_event_name(func)) + + def set_event(self, func): + self.get_event(func).set() + + def clear_event(self, func): + self.get_event(func).clear() + + def wait_event(self, func, *args): + self.get_event(func).wait(*args) + + class CallSerializer(): def __init__(self): self.queue = Queue() thread.start_new_thread(self.call_functions, (),) + self.bug_catcher = ExceptionSerializer() def call_functions(self): while 1: func, args, kwargs = self.queue.get(block=True) func(*args, **kwargs) - def serialize_call(self, function): + def serialize_call(self, timeout=0.5): ''' a call to a function decorated will not have overlapping calls, i.e thread safe ''' - @wraps(function) - def decorator(*args, **kwargs): - self.queue.put((function, args, kwargs)) - return decorator + def state(function): + @wraps(function) + def decorator(*args, **kwargs): + # Function will let us know when it is done running + # This is done so we can catch exceptions raised + # in functions that are run within threads + mark_func = self.bug_catcher.mark_done(function) + self.queue.put((mark_func, args, kwargs)) + # wait for the function to finish and raise errors + self.bug_catcher.catch_and_raise(function, timeout) + return decorator + return state + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/system_hotkey-1.0.2/tests/test_hotkeys.py new/system_hotkey-1.0.3/tests/test_hotkeys.py --- old/system_hotkey-1.0.2/tests/test_hotkeys.py 2016-07-28 22:29:56.000000000 +0200 +++ new/system_hotkey-1.0.3/tests/test_hotkeys.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,55 +0,0 @@ -import os -import time - -import system_hotkey -hk = system_hotkey.SystemHotkey() - -CALLBACK_RESULT = [] - -def append_callback(key): - print('APPENDING ',key) - CALLBACK_RESULT.append(key) - -if os.name == 'nt': - from win32api import keybd_event - def send_event(key): # note if we change the time on checking our queue this sleep time will have to be changed - time.sleep(0.03) - keybd_event(int(hk.get_keycode(key)) , 0, 1, 0) - time.sleep(0.03) - - -keys_to_test = [('k',), - ('control','k'), - ('control','shift','k'), - ('control','shift','alt','k'), - ('control','shift','alt','super', 'k')] - -def test_register_args(): - '''If in callback mode the register function requires a callback for all hotkeys regiersted, - an error needs to be raised''' - _hk = system_hotkey.SystemHotkey() - try: - _hk.register((keys_to_test[0]),) - except TypeError: - pass - else: - assert("hk register with no callback worked when it shouldn't have") - -def test_register_1(): - ''' - Registers a hotkey without any modifiers, tests that the callback - was run - ''' - hk.register(keys_to_test[0], callback=lambda e: append_callback(keys_to_test[0])) # THIS IS THREADED SO TBD DOCUMENT just need to wait a bit before sending the first key or after pressing a key getting the result - send_event(keys_to_test[0][0]) - assert CALLBACK_RESULT[0] == keys_to_test[0] - - -def register_2(): - '''testing with one modifier''' - hk.register(keys_to_test[1], lambda msg: CALLBACK_RESULT[0].append(keys_to_test[0])) - send_event(keys_to_test[1]) - -#~ test_register_1() -#~ register_2() -