Giter VIP home page Giter VIP logo

Comments (12)

dlashua avatar dlashua commented on May 17, 2024

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.

from pyscript.

craigbarratt avatar craigbarratt commented on May 17, 2024

Yikes - thanks for the example. I've stripped it down to this, which definitely does the wrong thing. Somehow the two instances get task.sleep mixed up during the parallel execution. Will debug later today.

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:

__init__ 5 <custom_components.pyscript.eval.Test object at 0x109de9700>: startup trigger
waiting for 5 <custom_components.pyscript.eval.Test object at 0x109de9700>
__init__ 10 <custom_components.pyscript.eval.Test object at 0x109bbab80>: startup trigger
waiting for 10 <custom_components.pyscript.eval.Test object at 0x109bbab80>
finished waiting for 10 <custom_components.pyscript.eval.Test object at 0x109bbab80> (took 1.995sec)
finished waiting for 5 <custom_components.pyscript.eval.Test object at 0x109de9700> (took 13.011sec)

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

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.

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

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:

2020-10-10 05:47:19 INFO (MainThread) [custom_components.pyscript.file.test.startup_trigger] __init__ 10 <custom_components.pyscript.eval.Test object at 0x7fb066681850>: startup trigger
2020-10-10 05:47:19 INFO (MainThread) [custom_components.pyscript.file.test] waiting for 10 <custom_components.pyscript.eval.Test object at 0x7fb066681850>
2020-10-10 05:47:19 INFO (MainThread) [custom_components.pyscript.file.test.startup_trigger] __init__ 5 <custom_components.pyscript.eval.Test object at 0x7fb05b1076a0>: startup trigger
2020-10-10 05:47:19 INFO (MainThread) [custom_components.pyscript.file.test] waiting for 5 <custom_components.pyscript.eval.Test object at 0x7fb05b1076a0>
2020-10-10 05:47:24 INFO (MainThread) [custom_components.pyscript.file.test] finished waiting for 5 <custom_components.pyscript.eval.Test object at 0x7fb05b1076a0> (took 5.001sec)
2020-10-10 05:47:29 INFO (MainThread) [custom_components.pyscript.file.test] finished waiting for 10 <custom_components.pyscript.eval.Test object at 0x7fb066681850> (took 10.001sec)

However, that is with MY original test code commented out. As soon as I add my test code back in, your test code breaks:

2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_2: STARTING
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: STARTING
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.file.test.startup_trigger] __init__ 5 <custom_components.pyscript.eval.Test object at 0x7fb06d8ea430>: startup trigger
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.file.test] waiting for 5 <custom_components.pyscript.eval.Test object at 0x7fb06d8ea430>
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.file.test.startup_trigger] __init__ 10 <custom_components.pyscript.eval.Test object at 0x7fb067b289a0>: startup trigger
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.file.test] waiting for 10 <custom_components.pyscript.eval.Test object at 0x7fb067b289a0>
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager.startup_trigger] input_boolean.test_2: startup trigger
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_2: on
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager.startup_trigger] input_boolean.test_1: startup trigger
2020-10-10 05:52:22 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: on
2020-10-10 05:52:24 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: pending_off
2020-10-10 05:52:24 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: delay off for 5
2020-10-10 05:52:24 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: pending_off
2020-10-10 05:52:24 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: delay off for 5
2020-10-10 05:52:27 INFO (MainThread) [custom_components.pyscript.file.test] finished waiting for 10 <custom_components.pyscript.eval.Test object at 0x7fb067b289a0> (took 5.001sec)
2020-10-10 05:52:29 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: off
2020-10-10 05:52:29 INFO (MainThread) [custom_components.pyscript.apps.occupancy_manager] input_boolean.test_1: off
2020-10-10 05:52:32 INFO (MainThread) [custom_components.pyscript.file.test] finished waiting for 5 <custom_components.pyscript.eval.Test object at 0x7fb06d8ea430> (took 10.002sec)

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

Taking the content of my def startup() and moving it into def startup_trigger() causes the correct input_booleans to turn on and off, however, the time in task.sleep() is incorrect.

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

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.

from pyscript.

craigbarratt avatar craigbarratt commented on May 17, 2024

Thanks for the additional info. Yes, this is another bug related to variable scoping for closures, in this case specific to classes. self inside the closure is bound to self in __init__, but that binding is incorrectly across all instances of the class, rather than being unique per instance. So as you figured out, self is not unique across class instances when the class function contains a closure. I'm still thinking about how to best fix this - it's quite tricky.

from pyscript.

craigbarratt avatar craigbarratt commented on May 17, 2024

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.

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

Does it affect only closures in __init__ or anywhere in any method of the class?

from pyscript.

craigbarratt avatar craigbarratt commented on May 17, 2024

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.

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

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!

from pyscript.

dlashua avatar dlashua commented on May 17, 2024

This seems to be resolved with the latest commits. Closing.

from pyscript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.