With this PR we can now use a "mini-language" to inject event-related data into the key bindings' handlers
e.g. self.bind("1,2,3", "digit_pressed('$event.key')")
- My first implementation was using the Python "Format Specification Mini-Language", so the syntax was the following:
self.bind("1,2,3", "digit_pressed('{event.key}')") # <-- uses curly brackets for interpolation
But as I was writing some integration tests with various kinds of Python primitives passed to the key bindings handlers I realised that using this syntax was causing an issue: as it's based on curly brackets we could not use Python dicts or sets in the parameters we inject.
- This is why I ended up opting for a dedicated SDL that doesn't conflict with Python syntax for dicts or set literals. As I don't want to create fancy SDLs out of thin air that users have to learn, I simply based it on the Python Template strings' one.
So our previous expression becomes this:
self.bind("1,2,3", "digit_pressed('$event.key')") # <-- uses a "dollar" syntax for interpolation
# ...which enables this kind of things:
self.bind("1,2,3", "digit_pressed('$event.key', {'sender_name': 'sidebar'})") # <-- injects a Python dict
Limits of this "action binding mini-language"
As explained in one of my comments on the GitHub diff, I also realised that this "action binding mini-language" has some strong limits, which can be a good thing for security but also has limits.
We can pass $event.key
to the action method for example, but we cannot pass the instance of the Event itself, or the sender instance - because this SDL is based on ast.literal_eval
, which intentionally doesn't allow using custom objects.
I guess that time and dog-fooding will tell us if expressions such as $event.sender
(which is the string representation of the sender, rather than the sender instance) is enough, or if we should rather start thinking of something else? :slightly_smiling_face:
Potential future improvements: dependency injection?
One potential way to solve this would be to use "a la Pytest" / "a la FastAPI" dependency injection...
So if the action method declares a param named event
for example we detect that and inject the Event instance for this parameter. Same with a param named sender
, etc. :slightly_smiling_face:
Dependency injection would also allow users to not use error-prone "Python-syntax-in-a-string expressions" - i.e. it's easy to miss the syntax error in something like this:
self.bind("1,2,3", "digit_pressed('$event.key', {'sender_name: 'sidebar'})")
(a closing single quote is missing)
closes #496