Query a key press using ctypes - python

I'd like to query each key of a keyboard without using win32api. I have it working using win32api.GetAsyncKeyState(key), but I'd also like to add support for if the module is not installed.
So far I've found one piece of fully working code, though it seems a bit heavyweight as it'd require its own thread, and would need over 1600 separate functions as I want to catch each key no matter of modifiers (there are 14 possible combinations per key).
Here is the code I found, would anyone be able to either suggest an alternative or how to get around the modifier problem?
import ctypes
import ctypes.wintypes
import win32con
class GlobalHotKeys(object):
"""
Register a key using the register() method, or using the #register decorator
Use listen() to start the message pump
Example:
from globalhotkeys import GlobalHotKeys
#GlobalHotKeys.register(GlobalHotKeys.VK_F1)
def hello_world():
print 'Hello World'
GlobalHotKeys.listen()
"""
key_mapping = []
user32 = ctypes.windll.user32
MOD_ALT = win32con.MOD_ALT
MOD_CTRL = win32con.MOD_CONTROL
MOD_CONTROL = win32con.MOD_CONTROL
MOD_SHIFT = win32con.MOD_SHIFT
MOD_WIN = win32con.MOD_WIN
#classmethod
def register(cls, vk, modifier=0, func=None):
"""
vk is a windows virtual key code
- can use ord('X') for A-Z, and 0-1 (note uppercase letter only)
- or win32con.VK_* constants
- for full list of VKs see: http://msdn.microsoft.com/en-us/library/dd375731.aspx
modifier is a win32con.MOD_* constant
func is the function to run. If False then break out of the message loop
"""
# Called as a decorator?
if func is None:
def register_decorator(f):
cls.register(vk, modifier, f)
return f
return register_decorator
else:
cls.key_mapping.append((vk, modifier, func))
#classmethod
def listen(cls):
"""
Start the message pump
"""
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
if not cls.user32.RegisterHotKey(None, index, modifiers, vk):
raise Exception('Unable to register hot key: ' + str(vk) + ' error code is: ' + str(ctypes.windll.kernel32.GetLastError()))
try:
msg = ctypes.wintypes.MSG()
i = 0
while cls.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
(vk, modifiers, func) = cls.key_mapping[msg.wParam]
if not func:
break
func()
cls.user32.TranslateMessage(ctypes.byref(msg))
cls.user32.DispatchMessageA(ctypes.byref(msg))
finally:
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
cls.user32.UnregisterHotKey(None, index)
#classmethod
def _include_defined_vks(cls):
for item in win32con.__dict__:
item = str(item)
if item[:3] == 'VK_':
setattr(cls, item, win32con.__dict__[item])
#classmethod
def _include_alpha_numeric_vks(cls):
for key_code in (list (range(ord('A'), ord('Z') + 1)) + list(range(ord('0'), ord('9') + 1)) ):
setattr(cls, 'VK_' + chr(key_code), key_code)
GlobalHotKeys._include_defined_vks()
GlobalHotKeys._include_alpha_numeric_vks()
This is an example of how it'd be used to read a:
#GlobalHotKeys.register(ord('A'))
def a():
print 'a'
#GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_SHIFT)
def a_shift():
print 'shift + a'
#GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_CONTROL | GlobalHotKeys.MOD_SHIFT)
def a_ctrl_shift():
print 'ctrl + shift + a'
...
GlobalHotKeys.listen()

Turned out to be an extremely simple answer, I finally stumbled across it when trying to read the microsoft info for the GetKeyState function.
ctypes.windll.user32.GetKeyState(key)
The state will either be 0 or 1 when not pressed, and increase to something like 60000 when pressed, so to get a True/False result, checking for > 1 seems to do the trick.
GetAsyncKeyState also kinda works, but sometimes results in a negative number, and sometimes doesn't, so I thought it'd be best using the alternative.

Related

How do you obtain the body of a function?

I would need to separate the signature from the body of a function in Python, i.e. :
def mult(a, b):
"Multiplication"
if a or b is None:
# border case
return None
else:
return a * b
(the function is there for demonstration only).
I know that inspect.getsourcelines(mult) will get me the full code, but I would like to have only the body, i.e. less the signature and the docstring:
if a or b is None:
# border case
return None
else:
return a * b
Is there an elegant way to obtain that, ideally using Python3 built-in parsing tools, rather than string manipulation?
As I don't know any function that does this, here is a homemade function that should work.
I don't have time for more in-depth use-cases. So i'll leave my incomplete answer here.
def get_body(func):
""" Using the magic method __doc__, we KNOW the size of the docstring.
We then, just substract this from the total length of the function
"""
try:
lines_to_skip = len(func.__doc__.split('\n'))
except AttributeError:
lines_to_skip = 0
lines = getsourcelines(func)[0]
return ''.join( lines[lines_to_skip+1:] )
def ex_0(a, b):
""" Please !
Don't
Show
This
"""
if a or b is None:
# border case
return None
else:
return a * b
def ex_1(a, b):
''' Please !Don'tShowThis'''
if a or b is None:
# border case
return None
else:
return a * b
def ex_2(a, b):
if a or b is None:
# border case
return None
else:
return a * b
def ex_3(bullshit, hello):
pass
get_body(ex_0)
# if a or b is None:
# # border case
# return None
# else:
# return a * b
get_body(ex_1)
# if a or b is None:
# # border case
# return None
# else:
# return a * b
get_body(ex_2)
# if a or b is None:
# # border case
# return None
# else:
# return a * b
get_body(ex_3)
# pass
you can use this also
i = inspect.getsource(fun_name).index('\n')
j = inspect.getsource(fun_name).rindex(':',0,i)
print(inspect.getsource(fun_name)[j+1:])
If you know your code will always have a non-empty docstring like a above, You can do this with the built in inspect library.
The below snippet should get you acquainted with how to see the source code body of a function.
def hello_world():
"""Python port of original Fortran code"""
print("hello_world")
import inspect
source = inspect.getsource(hello_world)
doc = hello_world.__doc__
code = source.split(doc)
body = code[1]
print(body)
EDIT:
I thought about this some more, you can first remove the defintion line and then strip out the docstring if it exists:
def hello_world():
"""Python port of original Fortran code"""
print("hello_world")
import inspect
source = inspect.getsource(hello_world)
doc = hello_world.__doc__
code = source.split(':',1)
body= code[1].replace(doc, "")
body = body.replace('""""""',"")
print(body)
Thanks a lot for the inspiring answers! I tried to take this further and I found a solution that uses Python's ast facilities.
import inspect, ast
from astunparse import unparse
def mult(a,
b): # comment
"""
This is a 'doctstring', "hello"
"""
if not a is None or b is None:
# border case
return None
else:
return a * b
def get_body(f, doc_string=False):
"""
Get the body text of a function, i.e. without the signature.
NOTE: Comments are stripped.
"""
COMMENT_START = ("'", '"')
code = inspect.getsource(mult)
# print("Function's code:\n", code)
module_tree = ast.parse(code) # this creates a module
# print("Dump:\n", ast.dump(module_tree))
# the first element of the module is evidently the function:
function_body = module_tree.body[0].body
# strip the code lines to remove the additional lines:
lines = [unparse(code_line).strip() for code_line in function_body]
# for i, line in enumerate(lines): print("%s: %s" % (i, line.strip()))
# in principle the notion of body contains the docstring:
if not doc_string:
lines = (line for line in lines if not line.startswith(COMMENT_START))
return '\n'.join(lines).strip()
s =get_body(mult)
print("---------")
print(s)
Here is the result:
$ python3 function_body.py
---------
if ((not (a is None)) or (b is None)):
return None
else:
return (a * b)
You can choose if you want the docstring or not. The downside of this approach (which should not be a downside in my use case) is that comments are stripped.
I also left some commented print statements, in case someone would like to explore the various steps.

Pygments in QScintilla

Consider this mcve:
import math
import sys
import textwrap
import time
from pathlib import Path
from collections import defaultdict
from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
from PyQt5.Qt import *
from pygments import lexers, styles, highlight, formatters
from pygments.lexer import Error, RegexLexer, Text, _TokenType
from pygments.style import Style
EXTRA_STYLES = {
"monokai": {
"background": "#272822",
"caret": "#F8F8F0",
"foreground": "#F8F8F2",
"invisibles": "#F8F8F259",
"lineHighlight": "#3E3D32",
"selection": "#49483E",
"findHighlight": "#FFE792",
"findHighlightForeground": "#000000",
"selectionBorder": "#222218",
"activeGuide": "#9D550FB0",
"misspelling": "#F92672",
"bracketsForeground": "#F8F8F2A5",
"bracketsOptions": "underline",
"bracketContentsForeground": "#F8F8F2A5",
"bracketContentsOptions": "underline",
"tagsOptions": "stippled_underline",
}
}
def convert_size(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return f"{s} {size_name[i]}"
class ViewLexer(QsciLexerCustom):
def __init__(self, lexer_name, style_name):
super().__init__()
# Lexer + Style
self.pyg_style = styles.get_style_by_name(style_name)
self.pyg_lexer = lexers.get_lexer_by_name(lexer_name, stripnl=False)
self.cache = {
0: ('root',)
}
self.extra_style = EXTRA_STYLES[style_name]
# Generate QScintilla styles
self.font = QFont("Consolas", 8, weight=QFont.Bold)
self.token_styles = {}
index = 0
for k, v in self.pyg_style:
self.token_styles[k] = index
if v.get("color", None):
self.setColor(QColor(f"#{v['color']}"), index)
if v.get("bgcolor", None):
self.setPaper(QColor(f"#{v['bgcolor']}"), index)
self.setFont(self.font, index)
index += 1
def defaultPaper(self, style):
return QColor(self.extra_style["background"])
def language(self):
return self.pyg_lexer.name
def get_tokens_unprocessed(self, text, stack=('root',)):
"""
Split ``text`` into (tokentype, text) pairs.
``stack`` is the inital stack (default: ``['root']``)
"""
lexer = self.pyg_lexer
pos = 0
tokendefs = lexer._tokens
statestack = list(stack)
statetokens = tokendefs[statestack[-1]]
while 1:
for rexmatch, action, new_state in statetokens:
m = rexmatch(text, pos)
if m:
if action is not None:
if type(action) is _TokenType:
yield pos, action, m.group()
else:
for item in action(lexer, m):
yield item
pos = m.end()
if new_state is not None:
# state transition
if isinstance(new_state, tuple):
for state in new_state:
if state == '#pop':
statestack.pop()
elif state == '#push':
statestack.append(statestack[-1])
else:
statestack.append(state)
elif isinstance(new_state, int):
# pop
del statestack[new_state:]
elif new_state == '#push':
statestack.append(statestack[-1])
else:
assert False, "wrong state def: %r" % new_state
statetokens = tokendefs[statestack[-1]]
break
else:
# We are here only if all state tokens have been considered
# and there was not a match on any of them.
try:
if text[pos] == '\n':
# at EOL, reset state to "root"
statestack = ['root']
statetokens = tokendefs['root']
yield pos, Text, u'\n'
pos += 1
continue
yield pos, Error, text[pos]
pos += 1
except IndexError:
break
def highlight_slow(self, start, end):
style = self.pyg_style
view = self.editor()
code = view.text()[start:]
tokensource = self.get_tokens_unprocessed(code)
self.startStyling(start)
for _, ttype, value in tokensource:
self.setStyling(len(value), self.token_styles[ttype])
def styleText(self, start, end):
view = self.editor()
t_start = time.time()
self.highlight_slow(start, end)
t_elapsed = time.time() - t_start
len_text = len(view.text())
text_size = convert_size(len_text)
view.setWindowTitle(f"Text size: {len_text} - {text_size} Elapsed: {t_elapsed}s")
def description(self, style_nr):
return str(style_nr)
class View(QsciScintilla):
def __init__(self, lexer_name, style_name):
super().__init__()
view = self
# -------- Lexer --------
self.setEolMode(QsciScintilla.EolUnix)
self.lexer = ViewLexer(lexer_name, style_name)
self.setLexer(self.lexer)
# -------- Shortcuts --------
self.text_size = 1
self.s1 = QShortcut(f"ctrl+1", view, self.reduce_text_size)
self.s2 = QShortcut(f"ctrl+2", view, self.increase_text_size)
# self.gen_text()
# # -------- Multiselection --------
self.SendScintilla(view.SCI_SETMULTIPLESELECTION, True)
self.SendScintilla(view.SCI_SETMULTIPASTE, 1)
self.SendScintilla(view.SCI_SETADDITIONALSELECTIONTYPING, True)
# -------- Extra settings --------
self.set_extra_settings(EXTRA_STYLES[style_name])
def get_line_separator(self):
m = self.eolMode()
if m == QsciScintilla.EolWindows:
eol = '\r\n'
elif m == QsciScintilla.EolUnix:
eol = '\n'
elif m == QsciScintilla.EolMac:
eol = '\r'
else:
eol = ''
return eol
def set_extra_settings(self, dct):
self.setIndentationGuidesBackgroundColor(QColor(0, 0, 255, 0))
self.setIndentationGuidesForegroundColor(QColor(0, 255, 0, 0))
if "caret" in dct:
self.setCaretForegroundColor(QColor(dct["caret"]))
if "line_highlight" in dct:
self.setCaretLineBackgroundColor(QColor(dct["line_highlight"]))
if "brackets_background" in dct:
self.setMatchedBraceBackgroundColor(QColor(dct["brackets_background"]))
if "brackets_foreground" in dct:
self.setMatchedBraceForegroundColor(QColor(dct["brackets_foreground"]))
if "selection" in dct:
self.setSelectionBackgroundColor(QColor(dct["selection"]))
if "background" in dct:
c = QColor(dct["background"])
self.resetFoldMarginColors()
self.setFoldMarginColors(c, c)
def increase_text_size(self):
self.text_size *= 2
self.gen_text()
def reduce_text_size(self):
if self.text_size == 1:
return
self.text_size //= 2
self.gen_text()
def gen_text(self):
content = Path(__file__).read_text()
while len(content) < self.text_size:
content *= 2
self.setText(content[:self.text_size])
if __name__ == '__main__':
app = QApplication(sys.argv)
view = View("python", "monokai")
view.setText(textwrap.dedent("""\
'''
Ctrl+1 = You'll decrease the size of existing text
Ctrl+2 = You'll increase the size of existing text
Warning: Check the window title to see how long it takes rehighlighting
'''
"""))
view.resize(800, 600)
view.show()
app.exec_()
To run it you need to install:
QScintilla==2.10.8
Pygments==2.3.1
PyQt5==5.12
I'm trying to figure out how to use pygments on a QScintilla widget and right now the main problem I need to solve is the performance when dealing with non-tiny documents.
I'd like the editor to become responsive & usable when dealing with large documents (>=100kb) but I don't know very well what's the approach I should take here. In order to test performance you can use Ctrl+1 or Ctrl+2 and the widget text will be decreased/increased respectively.
When I say "responsive" I mean that the highlighting computation of the visible screen should take no longer of [1-2]frame/highglight <=> [17-34]ms/highlight (assuming 60fps) so when typing you won't feel any slowdown.
Note: As you can see in the above mcve, I've included the pygments tokenizer so you can play around with it... it feels like in order to achieve "real-time highlighting" I'd need to use memoization/caching in some smart way but I'm struggling to figure out what's the data I need to cache and what's the best way to cache it... :/
Demo:
In the above demo you can see using this naive highlighting the editor will become unusable very soon, in my laptop rehighlighting text chunks of 32kb is still giving interactive framerate but with something higher than that the editor becomes completely unusable.
CONSIDERATIONS:
The most typical case will happen when you're typing/coding on the visible screen with no selections
It may happen you're editing multiple selections spread over the whole document, which means you won't know if these selections are near the visible screen or not. For instance, in Sublime when you press Alt+F3 you select all ocurrences under cursor
In the above snippet I've used a python lexer but the algorithm shouldn't focus too much on that one. Pygments support ~300 lexers afterall
The worst case scenario would happen if the visible screen is at the end of the file and one of the selections happens to live at the beginning of the screen... In case you need to rehighlight the whole document you'd need to find an alternative way even if that means the "highlighting" is not correct on the first pass
The most important is performance but also correctness... that is, if you give enough time the whole document should become highlighted correctly
REFERENCES:
https://qscintilla.com/styletext-the-highlighting-engine/
http://pygments.org/docs/
https://www.riverbankcomputing.com/static/Docs/QScintilla/annotated.html
The following documents are not specific to this particular problem but they talk about possible strategies of caching and syntax highlighting:
rope_science_11
https://raphlinus.github.io/personal/2017/10/12/what-im-working-on.html
syntax highlighting doc
https://code.visualstudio.com/blogs/2017/02/08/syntax-highlighting-optimizations
In highlight_slow, you're receiving start and end values, but you're ignoring the end value. As a result, any time you type a single character, the code is rehighlighting the entire rest of the buffer. This is why, if you type at the end of a long buffer, the timing is very fast - around .1 - .2 ms - but if you type at the beginning, it's very slow.
Thinking just in terms of correct highlighting, in most cases (with Python, at least) when you introduce a new character only the current line needs to be re-styled. Sometimes, like if you start a function definition or open a bracket, multiple lines might need to be styled. Only when you open or close a multiline """ or ''' string - will the rest of the buffer need to be restyled.
If you include start and end in your logging, you'll see that most of the time when you type they span a very small range. If you change one line of your highlight_code method from
code = view.text()[start:]
to
code = view.text()[start:end]
you'll see that the method almost always take sub-millisecond time now, and it almost always gets the highlighting correct.
From what I've been able to tell, this only gets the styling wrong when multiline quotes are involved. However, your current code has the same problem: try opening a multiline string, typing enter, and continuing the string on the next line. The second line will be highlighted as code. Qscintilla is leading you astray a bit here, by giving a start that does not include the beginning of the multiline quote. It's not trying to be perfect, though - the docs say
In fact, QScintilla says: “Hey, I think you should restyle the text between the character at position start up to the character at position end“. You are completely free to ignore this suggestion.
Handling mutliline quoting correctly will be a bit tricky! If it were me, and I wanted to get something working quickly, I'd probably impement a keystroke to refresh the highlighting for the entire buffer and use that when things look wrong.
If you're happy to write your own syntax highlighter, here's a possible way of speeding it up dramatically. You can do this with Pygments with a little effort; see the bottom of the answer for one possible way of doing this.
The syntax highlighter is simple. It has a small internal data structure, representing the current context, which it updates as it goes along. So, for the following Python code:
import time
def sleep_ms(ms):
"""sleeps for a length of time
given in milliseconds"""
time.sleep(
ms / 1000
)
sleep_ms(1000)
syntax error
its context might change like this, as it goes through the tokens¹:
>>> [nothing]
>>> IMPORT
IMPORT modulename
>>> [nothing]
>>> DEF
DEF functionname
DEF functionname, OPENPAREN
DEF functionname, OPENPAREN
DEF functionname ARGLIST
DEF functionname ARGLIST COLON
>>> FUNCBODY 4s
FUNCBODY 4s, DOUBLE_MLSTR
>>> FUNCBODY 4s, DOUBLE_MLSTR
FUNCBODY 4s
>>> FUNCBODY 4s
>>> FUNCBODY 4s, varname
FUNCBODY 4s, varname ATTR
FUNCBODY 4s, varname ATTR attrname
FUNCBODY 4s, varname ATTR attrname, OPENPAREN
>>> FUNCBODY 4s, varname ATTR attrname, OPENPAREN
>>> FUNCBODY 4s, varname ATTR attrname, OPENPAREN, varname
FUNCBODY 4s, varname ATTR attrname, OPENPAREN, TRUEDIV varname
FUNCBODY 4s, varname ATTR attrname, OPENPAREN, TRUEDIV varname intliteral
>>> FUNCBODY 4s, FUNCCALL
>>> FUNCBODY 4s
>>> [nothing]
varname
varname, OPENPAREN
varname, OPENPAREN, intliteral
FUNCCALL
>>> [nothing]
varname
ERROR
If you cache the final contexts of each line, then you can start the syntax highlighting at the line that changed and keep going until you get to a line where the context is the same as is cached; you don't have to recompute the whole file, but if you add something like """ then it'll recompute until the end. If you get to an ERROR then you can just stop there; there's no point recalculating the syntax highlighting past a syntax error, because you don't know what the context's meant to be. (For the initial version when you open the file, you could assume that there's no context after a syntax error; this heuristic seems to work well enough.)
This syntax highlighter has the potential to be ridiculously accurate, or just "good enough", with virtually no perceivable difference in speed between the two. Language-specific highlighters could even be dynamically linked plugins, and it'd still be reasonably fast! Additionally, if you add debouncing for highlighting of subsequent lines, typing """""" quickly enough will be just as fast as typing "" or 42, no matter how big the file is.
Note that this highlighter is single-pass – it doesn't highlight known variable names differently to unknown ones, for example. If you wish to do this, the problem becomes considerably harder.
¹: This example Python highlighter is a "ridiculously accurate" one; I probably wouldn't go with something like this if I had a time limit. Nevertheless, I've got it planned out in my head and – for now, at least – could explain it in detail if required.
Your code requires surprisingly few changes to work with this technique.
Change the beginning of your get_tokens_unprocessed to:
def get_tokens_unprocessed(self, text, stack=('root',), mutate_stack=False):
"""
Split ``text`` into (tokentype, text) pairs.
``stack`` is the inital stack (default: ``['root']``)
"""
lexer = self.pyg_lexer
pos = 0
tokendefs = lexer._tokens
if not mutate_stack:
statestack = list(stack)
statetokens = tokendefs[statestack[-1]]
Find some way of detecting the line number.
In highlight_slow's loop, do something like this (except better):
stack = list(self.cache[line_no_of(start)])
tokensource = self.get_tokens_unprocessed(code, stack, True)
self.startStyling(start)
pos = start;
for _, ttype, value in tokensource:
self.setStyling(len(value), self.token_styles[ttype])
pos += len(value)
if is_line_end(pos):
if pos >= end and stack == self.cache[line_no_of(start)]:
break
self.cache[line_no_of(start)] = tuple(stack)
Obviously, the code would have to be better than this, and you'd have to find some efficient way of implementing is_line_end and line_no_of; there's probably some Pygments way of doing this.
This solution has at least one benefit over yours already: it supports multi-line comments.

How to create a global hotkey on Windows with 3 arguments?

Just like Ctl, Alt + delete
I want to write a program, which uses global hotkeys with 3 or more arguments in python. The assigned function should only perform when I press all three keys on my keyboard. For example alt, windows and F3.
win32con.VK_F3, win32con.MOD_WIN, win32con.VK_F5
This is the current program I want to run, however its output is:
Traceback (most recent call last):
File "C:\Python32\Syntax\hot keys\hotkeys2.py", line 41, in <module>
for id, (vk, modifiers) in HOTKEYS.items ():
ValueError: too many values to unpack (expected 2)
The Program:
import os
import sys
import ctypes
from ctypes import wintypes
import win32con
byref = ctypes.byref
user32 = ctypes.windll.user32
HOTKEYS = {
1 : (win32con.VK_F3, win32con.MOD_WIN, win32con.VK_F5),
2 : (win32con.VK_F4, win32con.MOD_WIN),
3 : (win32con.VK_F2, win32con.MOD_WIN)
}
def handle_win_f3 ():
#os.startfile (os.environ['TEMP'])
print ("Hello WOrld! F3")
def handle_win_f4 ():
#user32.PostQuitMessage (0)
print ("Hello WOrld! F4")
def handle_win_f1_escape ():
print("exit")
sys.exit()
HOTKEY_ACTIONS = {
1 : handle_win_f3,
2 : handle_win_f4,
3 : handle_win_f1_escape
}
for id, (vk, modifiers) in HOTKEYS.items ():
print ("Registering id", id, "for key", vk)
if not user32.RegisterHotKey (None, id, modifiers, vk):
print ("Unable to register id", id)
try:
msg = wintypes.MSG ()
while user32.GetMessageA (byref (msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
action_to_take = HOTKEY_ACTIONS.get (msg.wParam)
#print(" msg.message == win32con.WM_HOTKEY:")
if action_to_take:
action_to_take ()
user32.TranslateMessage (byref (msg))
user32.DispatchMessageA (byref (msg))
finally:
for id in HOTKEYS.keys ():
user32.UnregisterHotKey (None, id)
print("user32.UnregisterHotKey (None, id)")
Registering 3 hotkeys? Possible?
Explains how one can use assign one key that needs to be pressed and then if two of which either needs to be pressed. However I won’t that the function only performs when all there are pressed simultaneously. I took
For anyone interested in details and a more elaborate example regarding this topic, I recently wrote a short program demonstrating the hotkey functions provided by win32con. The program allows you to specify and test any hotkeys you want via command line:
Usage: python.exe hotkey.py MOD_ALT VK_UP -> test hotkey ALT + Arrow UP
# Imports
import win32con
import ctypes, ctypes.wintypes
import sys
#
# Functions
#
def dispatch_hotkey(msg):
mod = msg.lParam & 0b1111111111111111
key = msg.lParam >> 16
bit = bin(msg.lParam)[2:]
print("\n*** Received hotkey message (wParam: %d, lParam: %d)" % (msg.wParam, msg.lParam))
print("lParam bitmap: %s" % bit)
print("lParam low-word (modifier): %d, high-word (key): %d" % (mod, key))
print("-> Hotkey %s with modifier %s detected\n" % (keys[key], mods[mod]))
#
# Main
#
# Build translation maps (virtual key codes / modifiers to string)
# Note: exec() is a hack and should not be used in real programs!!
print("\n*** Building translation maps")
mods = {}
keys = {}
for item in dir(win32con):
if item.startswith("MOD_"):
exec("mods[item] = win32con." + item)
exec("mods[win32con." + item + "] = '" + item + "'")
if item.startswith("VK_"):
exec("keys[item] = win32con." + item)
exec("keys[win32con." + item + "] = '" + item + "'")
# Process command line
print("\n*** Processing command line")
mod = "MOD_WIN"
key = "VK_ESCAPE"
for param in sys.argv:
if param.startswith("MOD_"):
if param in mods: mod = param
else: print("\nInvalid modifier specified (%s). Using default.\n-> Use '--list-mods' for a list of valid modifiers." % param)
if param.startswith("VK_"):
if param in keys: key = param
else: print("\nInvalid key specified (%s). Using default.\n-> Use '--list-keys' for a list of valid keys." % param)
if "--list-mods" in sys.argv:
print("\nAvailable modifiers:")
for item in dir(win32con):
if item.startswith("MOD_"): sys.stdout.write(item + ", ")
print("\b\b ")
if "--list-keys" in sys.argv:
print("\nAvailable keys:")
for item in dir(win32con):
if item.startswith("VK_"): sys.stdout.write(item + ", ")
print("\b\b ")
# Register hotkey
print("\n*** Registering global hotkey (modifier: %s, key: %s)" % (mod, key))
ctypes.windll.user32.RegisterHotKey(None, 1, mods[mod], keys[key])
# Wait for hotkey to be triggered
print("\n*** Waiting for hotkey message...")
try:
msg = ctypes.wintypes.MSG()
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
dispatch_hotkey(msg)
break
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
# Unregister hotkey
finally:
ctypes.windll.user32.UnregisterHotKey(None, 1)
Please note that this program is intended for demonstration purposes only as parts of the program (e.g. exec function) should not be used in production environments. Also note that with this approach, you won't be able to override built-in hotkeys like WIN + E etc., they will simply be ignored and still perform the built-in functions (e.g. opening Explorer).
Another Way (courtesy of #martineau)
Here's how to build the translation maps without using exec():
print("\n*** Building translation maps")
mods = {}
keys = {}
for item, value in vars(win32con).items():
if item.startswith("MOD_"):
mods[item] = value
mods[value] = item
elif item.startswith("VK_"):
keys[item] = value
keys[value] = item
For starters, if you wanted alt, windows and F3, wouldn't you need to use win32con.VK_F3, win32con.MOD_ALT, win32con.MOD_WIN for the HOTKEYS entry?
However, it doesn't really make sense to say press F3 with modifiers of the Win and F5 key.
The error on the line:
for id, (vk, modifiers) in HOTKEYS.items ():
is because the value of each dictionary entry is a variable length tuple. Here's a way to handle that which also bitwise OR s all the modifier values together in preparation for passing them as a single argument to RegisterHotKey().
from functools import reduce
for id, values in HOTKEYS.items ():
vk, modifiers = values[0], reduce (lambda x, y: x | y, values[1:])
print ("Registering id", id, "for key", vk)
if not user32.RegisterHotKey (None, id, modifiers, vk):
print ("Unable to register id", id)
It would have been easier to work on your problem if your code was indented properly and followed the PEP 8 -- Style Guide for Python Code recommendations. Please consider doing so in the future.

How can I get the values of the locals of a function after it has been executed?

Suppose I have a function like f(a, b, c=None). The aim is to call the function like f(*args, **kwargs), and then construct a new set of args and kwargs such that:
If the function had default values, I should be able to acquire their values. For example, if I call it like f(1, 2), I should be able to get the tuple (1, 2, None) and/or the dictionary {'c': None}.
If the value of any of the arguments was modified inside the function, get the new value. For example, if I call it like f(1, 100000, 3) and the function does if b > 500: b = 5 modifying the local variable, I should be able to get the the tuple (1, 5, 3).
The aim here is to create a a decorator that finishes the job of a function. The original function acts as a preamble setting up the data for the actual execution, and the decorator finishes the job.
Edit: I'm adding an example of what I'm trying to do. It's a module for making proxies for other classes.
class Spam(object):
"""A fictional class that we'll make a proxy for"""
def eggs(self, start, stop, step):
"""A fictional method"""
return range(start, stop, step)
class ProxyForSpam(clsproxy.Proxy):
proxy_for = Spam
#clsproxy.signature_preamble
def eggs(self, start, stop, step=1):
start = max(0, start)
stop = min(100, stop)
And then, we'll have that:
ProxyForSpam().eggs(-10, 200) -> Spam().eggs(0, 100, 1)
ProxyForSpam().eggs(3, 4) -> Spam().eggs(3, 4, 1)
There are two recipes available here, one which requires an external library and another that uses only the standard library. They don't quite do what you want, in that they actually modify the function being executed to obtain its locals() rather than obtain the locals() after function execution, which is impossible, since the local stack no longer exists after the function finishes execution.
Another option is to see what debuggers, such as WinPDB or even the pdb module do. I suspect they use the inspect module (possibly along with others), to get the frame inside which a function is executing and retrieve locals() that way.
EDIT: After reading some code in the standard library, the file you want to look at is probably bdb.py, which should be wherever the rest of your Python standard library is. Specifically, look at set_trace() and related functions. This will give you an idea of how the Python debugger breaks into the class. You might even be able to use it directly. To get the frame to pass to set_trace() look at the inspect module.
I've stumbled upon this very need today and wanted to share my solution.
import sys
def call_function_get_frame(func, *args, **kwargs):
"""
Calls the function *func* with the specified arguments and keyword
arguments and snatches its local frame before it actually executes.
"""
frame = None
trace = sys.gettrace()
def snatch_locals(_frame, name, arg):
nonlocal frame
if frame is None and name == 'call':
frame = _frame
sys.settrace(trace)
return trace
sys.settrace(snatch_locals)
try:
result = func(*args, **kwargs)
finally:
sys.settrace(trace)
return frame, result
The idea is to use sys.trace() to catch the frame of the next 'call'. Tested on CPython 3.6.
Example usage
import types
def namespace_decorator(func):
frame, result = call_function_get_frame(func)
try:
module = types.ModuleType(func.__name__)
module.__dict__.update(frame.f_locals)
return module
finally:
del frame
#namespace_decorator
def mynamespace():
eggs = 'spam'
class Bar:
def hello(self):
print("Hello, World!")
assert mynamespace.eggs == 'spam'
mynamespace.Bar().hello()
I don't see how you could do this non-intrusively -- after the function is done executing, it doesn't exist any more -- there's no way you can reach inside something that doesn't exist.
If you can control the functions that are being used, you can do an intrusive approach like
def fn(x, y, z, vars):
'''
vars is an empty dict that we use to pass things back to the caller
'''
x += 1
y -= 1
z *= 2
vars.update(locals())
>>> updated = {}
>>> fn(1, 2, 3, updated)
>>> print updated
{'y': 1, 'x': 2, 'z': 6, 'vars': {...}}
>>>
...or you can just require that those functions return locals() -- as #Thomas K asks above, what are you really trying to do here?
Witchcraft below read on your OWN danger(!)
I have no clue what you want to do with this, it's possible but it's an awful hack...
Anyways, I HAVE WARNED YOU(!), be lucky if such things don't work in your favorite language...
from inspect import getargspec, ismethod
import inspect
def main():
#get_modified_values
def foo(a, f, b):
print a, f, b
a = 10
if a == 2:
return a
f = 'Hello World'
b = 1223
e = 1
c = 2
foo(e, 1000, b = c)
# intercept a function and retrieve the modifed values
def get_modified_values(target):
def wrapper(*args, **kwargs):
# get the applied args
kargs = getcallargs(target, *args, **kwargs)
# get the source code
src = inspect.getsource(target)
lines = src.split('\n')
# oh noes string patching of the function
unindent = len(lines[0]) - len(lines[0].lstrip())
indent = lines[0][:len(lines[0]) - len(lines[0].lstrip())]
lines[0] = ''
lines[1] = indent + 'def _temp(_args, ' + lines[1].split('(')[1]
setter = []
for k in kargs.keys():
setter.append('_args["%s"] = %s' % (k, k))
i = 0
while i < len(lines):
indent = lines[i][:len(lines[i]) - len(lines[i].lstrip())]
if lines[i].find('return ') != -1 or lines[i].find('return\n') != -1:
for e in setter:
lines.insert(i, indent + e)
i += len(setter)
elif i == len(lines) - 2:
for e in setter:
lines.insert(i + 1, indent + e)
break
i += 1
for i in range(0, len(lines)):
lines[i] = lines[i][unindent:]
data = '\n'.join(lines) + "\n"
# setup variables
frame = inspect.currentframe()
loc = inspect.getouterframes(frame)[1][0].f_locals
glob = inspect.getouterframes(frame)[1][0].f_globals
loc['_temp'] = None
# compile patched function and call it
func = compile(data, '<witchstuff>', 'exec')
eval(func, glob, loc)
loc['_temp'](kargs, *args, **kwargs)
# there you go....
print kargs
# >> {'a': 10, 'b': 1223, 'f': 'Hello World'}
return wrapper
# from python 2.7 inspect module
def getcallargs(func, *positional, **named):
"""Get the mapping of arguments to values.
A dict is returned, with keys the function argument names (including the
names of the * and ** arguments, if any), and values the respective bound
values from 'positional' and 'named'."""
args, varargs, varkw, defaults = getargspec(func)
f_name = func.__name__
arg2value = {}
# The following closures are basically because of tuple parameter unpacking.
assigned_tuple_params = []
def assign(arg, value):
if isinstance(arg, str):
arg2value[arg] = value
else:
assigned_tuple_params.append(arg)
value = iter(value)
for i, subarg in enumerate(arg):
try:
subvalue = next(value)
except StopIteration:
raise ValueError('need more than %d %s to unpack' %
(i, 'values' if i > 1 else 'value'))
assign(subarg,subvalue)
try:
next(value)
except StopIteration:
pass
else:
raise ValueError('too many values to unpack')
def is_assigned(arg):
if isinstance(arg,str):
return arg in arg2value
return arg in assigned_tuple_params
if ismethod(func) and func.im_self is not None:
# implicit 'self' (or 'cls' for classmethods) argument
positional = (func.im_self,) + positional
num_pos = len(positional)
num_total = num_pos + len(named)
num_args = len(args)
num_defaults = len(defaults) if defaults else 0
for arg, value in zip(args, positional):
assign(arg, value)
if varargs:
if num_pos > num_args:
assign(varargs, positional[-(num_pos-num_args):])
else:
assign(varargs, ())
elif 0 < num_args < num_pos:
raise TypeError('%s() takes %s %d %s (%d given)' % (
f_name, 'at most' if defaults else 'exactly', num_args,
'arguments' if num_args > 1 else 'argument', num_total))
elif num_args == 0 and num_total:
raise TypeError('%s() takes no arguments (%d given)' %
(f_name, num_total))
for arg in args:
if isinstance(arg, str) and arg in named:
if is_assigned(arg):
raise TypeError("%s() got multiple values for keyword "
"argument '%s'" % (f_name, arg))
else:
assign(arg, named.pop(arg))
if defaults: # fill in any missing values with the defaults
for arg, value in zip(args[-num_defaults:], defaults):
if not is_assigned(arg):
assign(arg, value)
if varkw:
assign(varkw, named)
elif named:
unexpected = next(iter(named))
if isinstance(unexpected, unicode):
unexpected = unexpected.encode(sys.getdefaultencoding(), 'replace')
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(f_name, unexpected))
unassigned = num_args - len([arg for arg in args if is_assigned(arg)])
if unassigned:
num_required = num_args - num_defaults
raise TypeError('%s() takes %s %d %s (%d given)' % (
f_name, 'at least' if defaults else 'exactly', num_required,
'arguments' if num_required > 1 else 'argument', num_total))
return arg2value
main()
Output:
1 1000 2
{'a': 10, 'b': 1223, 'f': 'Hello World'}
There you go... I'm not responsible for any small children that get eaten by demons or something the like (or if it breaks on complicated functions).
PS: The inspect module is the pure EVIL.
Since you are trying to manipulate variables in one function, and do some job based on those variables on another function, the cleanest way to do it is having these variables to be an object's attributes.
It could be a dictionary - that could be defined inside the decorator - therefore access to it inside the decorated function would be as a "nonlocal" variable. That cleans up the default parameter tuple of this dictionary, that #bgporter proposed.:
def eggs(self, a, b, c=None):
# nonlocal parms ## uncomment in Python 3
parms["a"] = a
...
To be even more clean, you probably should have all these parameters as attributes of the instance (self) - so that no "magical" variable has to be used inside the decorated function.
As for doing it "magically" without having the parameters set as attributes of certain object explicitly, nor having the decorated function to return the parameters themselves (which is also an option) - that is, to have it to work transparently with any decorated function - I can't think of a way that does not involve manipulating the bytecode of the function itself.
If you can think of a way to make the wrapped function raise an exception at return time, you could trap the exception and check the execution trace.
If it is so important to do it automatically that you consider altering the function bytecode an option, feel free to ask me further.

Real-world examples of nested functions

I asked previously how the nested functions work, but unfortunately I still don't quite get it. To understand it better, can someone please show some real-wold, practical usage examples of nested functions?
Many thanks
Your question made me curious, so I looked in some real-world code: the Python standard library. I found 67 examples of nested functions. Here are a few, with explanations.
One very simple reason to use a nested function is simply that the function you're defining doesn't need to be global, because only the enclosing function uses it. A typical example from Python's quopri.py standard library module:
def encode(input, output, quotetabs, header = 0):
...
def write(s, output=output, lineEnd='\n'):
# RFC 1521 requires that the line ending in a space or tab must have
# that trailing character encoded.
if s and s[-1:] in ' \t':
output.write(s[:-1] + quote(s[-1]) + lineEnd)
elif s == '.':
output.write(quote(s) + lineEnd)
else:
output.write(s + lineEnd)
... # 35 more lines of code that call write in several places
Here there was some common code within the encode function, so the author simply factored it out into a write function.
Another common use for nested functions is re.sub. Here's some code from the json/encode.py standard library module:
def encode_basestring(s):
"""Return a JSON representation of a Python string
"""
def replace(match):
return ESCAPE_DCT[match.group(0)]
return '"' + ESCAPE.sub(replace, s) + '"'
Here ESCAPE is a regular expression, and ESCAPE.sub(replace, s) finds all matches of ESCAPE in s and replaces each one with replace(match).
In fact, any API, like re.sub, that accepts a function as a parameter can lead to situations where nested functions are convenient. For example, in turtle.py there's some silly demo code that does this:
def baba(xdummy, ydummy):
clearscreen()
bye()
...
tri.write(" Click me!", font = ("Courier", 12, "bold") )
tri.onclick(baba, 1)
onclick expects you to pass an event-handler function, so we define one and pass it in.
Decorators are a very popular use for nested functions. Here's an example of a decorator that prints a statement before and after any call to the decorated function.
def entry_exit(f):
def new_f(*args, **kwargs):
print "Entering", f.__name__
f(*args, **kwargs)
print "Exited", f.__name__
return new_f
#entry_exit
def func1():
print "inside func1()"
#entry_exit
def func2():
print "inside func2()"
func1()
func2()
print func1.__name__
Nested functions avoid cluttering other parts of the program with other functions and variables that only make sense locally.
A function that return Fibonacci numbers could be defined as follows:
>>> def fib(n):
def rec():
return fib(n-1) + fib(n-2)
if n == 0:
return 0
elif n == 1:
return 1
else:
return rec()
>>> map(fib, range(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
EDIT: In practice, generators would be a better solution for this, but the example shows how to take advantage of nested functions.
They are useful when using functions that take other functions as input. Say you're in a function, and want to sort a list of items based on the items' value in a dict:
def f(items):
vals = {}
for i in items: vals[i] = random.randint(0,100)
def key(i): return vals[i]
items.sort(key=key)
You can just define key right there and have it use vals, a local variable.
Another use-case is callbacks.
I have only had to use nested functions when creating decorators. A nested function is basically a way of adding some behavior to a function without knowing what the function is that you are adding behavior to.
from functools import wraps
from types import InstanceType
def printCall(func):
def getArgKwargStrings(*args, **kwargs):
argsString = "".join(["%s, " % (arg) for arg in args])
kwargsString = "".join(["%s=%s, " % (key, value) for key, value in kwargs.items()])
if not len(kwargs):
if len(argsString):
argsString = argsString[:-2]
else:
kwargsString = kwargsString[:-2]
return argsString, kwargsString
#wraps(func)
def wrapper(*args, **kwargs):
ret = None
if args and isinstance(args[0], InstanceType) and getattr(args[0], func.__name__, None):
instance, args = args[0], args[1:]
argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
ret = func(instance, *args, **kwargs)
print "Called %s.%s(%s%s)" % (instance.__class__.__name__, func.__name__, argsString, kwargsString)
print "Returned %s" % str(ret)
else:
argsString, kwargsString = getArgKwargStrings(*args, **kwargs)
ret = func(*args, **kwargs)
print "Called %s(%s%s)" % (func.__name__, argsString, kwargsString)
print "Returned %s" % str(ret)
return ret
return wrapper
def sayHello(name):
print "Hello, my name is %s" % (name)
if __name__ == "__main__":
sayHelloAndPrintDebug = printCall(sayHello)
name = "Nimbuz"
sayHelloAndPrintDebug(name)
Ignore all the mumbo jumbo in the "printCall" function for right now and focus only the "sayHello" function and below. What we're doing here is we want to print out how the "sayHello" function was called everytime it is called without knowing or altering what the "sayHello" function does. So we redefine the "sayHello" function by passing it to "printCall", which returns a NEW function that does what the "sayHello" function does AND prints how the "sayHello" function was called. This is the concept of decorators.
Putting "#printCall" above the sayHello definition accomplishes the same thing:
#printCall
def sayHello(name):
print "Hello, my name is %s" % (name)
if __name__ == "__main__":
name = "Nimbuz"
sayHello(name)
Yet another (very simple) example. A function that returns another function. Note how the inner function (that is returned) can use variables from the outer function's scope.
def create_adder(x):
def _adder(y):
return x + y
return _adder
add2 = create_adder(2)
add100 = create_adder(100)
>>> add2(50)
52
>>> add100(50)
150
Python Decorators
This is actually another topic to learn, but if you look at the stuff on 'Using Functions as Decorators', you'll see some examples of nested functions.
OK, besides decorators: Say you had an application where you needed to sort a list of strings based on substrings which varied from time to time. Now the sorted functions takes a key= argument which is a function of one argument: the items (strings in this case) to be sorted. So how to tell this function which substrings to sort on? A closure or nested function, is perfect for this:
def sort_key_factory(start, stop):
def sort_key(string):
return string[start: stop]
return sort_key
Simple eh? You can expand on this by encapsulating start and stop in a tuple or a slice object and then passing a sequence or iterable of these to the sort_key_factory.

Categories