-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
multiple instances of the same class conflict #33
Comments
And, as an added point of debugging, swapping the order of these in configuration.yaml does what you'd expect... test_1 eventually turns off, while test_2 logs up to "on" and then never turns off. |
Yikes - thanks for the example. I've stripped it down to this, which definitely does the wrong thing. Somehow the two instances get import time
registered_triggers = []
class Test:
def __init__(self, timeout):
self.timeout = timeout
@time_trigger('startup')
def startup_trigger():
log.info(f'__init__ {self.timeout} {self}: startup trigger')
self.wait()
registered_triggers.append(startup_trigger)
def wait(self):
log.info(f"waiting for {self.timeout} {self}")
t0 = time.monotonic()
task.sleep(self.timeout)
delay = time.monotonic() - t0
log.info(f"finished waiting for {self.timeout} {self} (took {delay:.3f}sec)")
t1 = Test(5)
task.sleep(3)
t2 = Test(10) output shows last two statements have the wrong delay:
|
Excellent. I'm glad you were able to reproduce. In my version, it was a bit harder to watch the logs, but, as best as I can tell, one of the tasks actually stops running (either it doesn't trigger or it finds an exception that doesn't make it to the logs). In fact, I thought I was looking at a task.unique() bug at first, because I was using it in my code and it is supposed to kill other tasks. Part of the reason I stripped it down like I did was to remove any use of task.unique to see if the problem still persisted. But, before all of that was done, I could see in the logs that 2 (of the 4) instances of this app wouldn't even get to the task.sleep() call in some iterations of my testing. No error, it would just not issue the next log line that should have occurred on the next tick for that particular chain of events. |
In an attempt to grok enough of the AST bits to fix this myself, I have run the code you pasted above. For me, it's working fine:
However, that is with MY original test code commented out. As soon as I add my test code back in, your test code breaks:
|
Taking the content of my |
As expected, though it can't hurt to confirm, converting my test case as well as my actual application to not use a class at all and, instead, pass around a data dict to non-class defined functions causes everything to work exactly as I expect it would with no issues. |
Thanks for the additional info. Yes, this is another bug related to variable scoping for closures, in this case specific to classes. |
Here's another test case that exhibits the bug (at least for me): class ClosureInstanceBug:
def __init__(self, name):
self.name = name
log.info(f"__init__ {self} {self.name}")
task.sleep(2)
def closure():
self.any_function()
log.info(f"func {self} {self.name}")
@time_trigger("startup")
def func1():
c1 = ClosureInstanceBug("c1")
@time_trigger("startup")
def func2():
task.sleep(1)
c2 = ClosureInstanceBug("c2") When you run it, c2 finishes before c1. |
Does it affect only closures in __init__ or anywhere in any method of the class? |
Scratch that. The variable binding in closures inside class instances turned out to be correct. The bug was that the execution context was being bound to the class methods when the class was defined. So the two async functions were calling their instance's methods with a common execution context, which includes the symbol table, so bad things happen. Should be fixed now with 8dbde8b. |
Initial tests show that it is working correctly. I'm going to leave it running live so I can SEE it working and report back after a bit. Thanks for digging through this! |
This seems to be resolved with the latest commits. Closing. |
It's taken a bit of effort to boil this down to only the part that seems to be broken. Hopefully, this will be broken for you too.
I've put the pieces in a gist so it's a bit easier to read and copy:
https://gist.github.com/dlashua/67f3accdaf9bac8e4ed056196b039ae4
Notice that the log lines stop showing input_boolean.test_1 once the "on" message is received. Also, in Home Assistant, input_boolean.test_1 never turns off.
The text was updated successfully, but these errors were encountered: