question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Request: use state_trigger with a passed function

See original GitHub issue

I find I use this pattern quite a bit (untested):

registered_triggers = []

class FollowMe:
    def __init__(self, config):
        self.config = self.validate_config(config)

        @state_trigger(f'True or {self.config["input"]}')
        def state_trigger_update():
            self.update()

        registered_triggers.append(state_trigger_update)

    def validate_config(self, config):
        # do better
        return config

    def update(self):
        if state.get(config["input"]) == 'on':
            homeassistant.turn_on(entity_id=self.config['output'])
        else:
            homeassistant.turn_off(entity_id=self.config['output'])

It would be cleaner if I could do this:

class FollowMe:
    def __init__(self, config):
        pyscript.state_trigger(f'True or {self.config["input"]}')(self.update)

The pyscript.state_trigger method could be named something else if that made more sense. It could also take the function as a parameter (named or positional) if that’s better. And this same functionality would be useful on all the decorators.

I can implement something similar in code myself like this (untested):

registered_triggers = []

def make_state_trigger(trigger, cb):
    @state_trigger(trigger)
    def inner_state_trigger():
        cb()
    
    registered_triggers.append(inner_state_trigger)

class FollowMe:
    def __init__(self, config):
        self.config = self.validate_config(config)

        make_state_trigger(
          f'True or {self.config["input"]}',
          self.update
        )

… It’s just more readable, useful, and documented if it’s built-in.

It would also be amazing if these triggers were removable. Something like this:

registered_triggers = {}

def make_state_trigger(trigger, cb):
    # make some unique ID in a better way
    unique_id = time.time()

    @state_trigger(trigger)
    def inner_state_trigger():
        cb()
    
    registered_triggers[unique_id] = inner_state_trigger

    def cancel_trigger():
        del registered_triggers[unique_id]

    return cancel_trigger


# in some class
cancel = make_state_trigger('domain.entity == "on"', self.update)

# later
cancel()

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
craigbarrattcommented, Oct 16, 2020

This relates to your comments in #30 - as you noted, pyscript doesn’t yet support decorators, other than the builtin ones, which are hardcoded.

It seems like I should support decorators, and then turn the currently hardcoded decorators into functions that can be used as the current decorators, but also as you propose above.

One immediate issue, is that currently when you specify multiple decorators, it’s easy for the code to know when all of them have been processed and it can then “arm” the trigger. In the function call case, you could have several nested functions with different triggers, and only the last one should arm the trigger. And the order shouldn’t matter. I’m not sure yet how to deal with those issues.

A second issue is that with decorators, keeping a pointer to the function is still up to the user. It’s harder to see how to do that automatically. Maybe there is a @persistent decorator that does that, and returns a cancel function? I need to think about this some more.

0reactions
dlashuacommented, Dec 10, 2020

I’ve created a piece of working example code to follow another path for this. I also realize that I missed an important piece in my example code above… returning the handler. Ooops. The code below has actually been tested and is working.

The concept is, each decorator can be a “filter” (like state_active), a “generator” (like state_trigger), or a modifier (which pyscript doesn’t do (yet?), but I left those bits in anyway so you could see them).

The “func_vars” is what would get passed around (instead of the “value” I’m using in this example). And, of course, instead of an event emitter, we have the HASS buss for states and events.

https://gist.github.com/dlashua/b2f510a794cedfb370a0418b8592765e

Each class, when __call__ed, stores the function it was passed and returns its handler. This allows the decorators to be chained.

With this in place, the biggest difference between how pyscript works now and how it would work with this is that the location of “state_active” in the chain would matter. Since “state_trigger” would be “generating” messages, if “state_active” was above it, then it would never be consulted. Some more complex code could be written to allow state_active to pass its filter handlers down the chain with the handler checking to see if it’s the last in the chain and calling all of the filters first if it is. But, this wouldn’t work well when also faced with user supplied decorators because they’d have to extend a certain object type to be detectable. If this is important to you, though, I can work out the details and provide sample code on how that could work as well.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Reference — hacs-pyscript 1.3.3 documentation
Typically common functions or features would be implemented in a module or ... You can specify a state trigger on any change with...
Read more >
VisualState Class (Windows.UI.Xaml) - UWP - Microsoft Learn
Represents the visual appearance of a UI element when it is in a specific state. Visual states use Setters or a Storyboard to...
Read more >
Pass a parameter to a fixture function - Stack Overflow
Another way to do this is to use the request object to access variables defined in the module or class the test function...
Read more >
Automation Trigger - Home Assistant
Numeric state trigger. Fires when the numeric value of an entity's state (or attribute's value if using the attribute property, or the calculated...
Read more >
Functions - JavaScript - MDN Web Docs
Values can be passed to a function, and the function will return a ... As all other objects, Function objects can be created...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found