0

I have three modules, module1.py, module2.py, and module3.py, where module2.py contains a simple flag.

I need a function in module1.py to wait for another function in module3.py to successfully begin (as is indicated by a change in flag value in module2.py). If it helps, module3.py's execution is dependent on whether or not a successful connection is made to an external Bluetooth device.

I'm having an issue where module1.py is not waiting for module3.py to change the flag value in module2.py, and is instead immediately executing code based on the default value of the flag in module2.py, which is False.

I need a way for module1.py to wait for module3.py to change the flag value to True if module3.py begins successfully, and if after a certain amount of time (perhaps 30 seconds) the value of the flag is not changed, then the function in module1.py ceases execution.

Here is some sample code:

module1.py

import module2

def foo():
    try:
        subprocess.Popen(["python", "module3.py"], cwd=os.getcwd()
        if module2.flag:
           print('module3.py started successfully!')
           someOtherFunction()
        else:
           print('module3.py died')
    except Exception as e:
        print('something happened! ', e)

module2.py

flag = False

module3.py

import module2

def run():
    # do some stuff #
    try:
        connect.ToDevice() # connection was made to device
        module2.flag = True
        doCoolStuff()
    except Exception as e: 
        print('startup failed: ', e) # connection to device failed


if __name__ == "__main__":
    # do more cool stuff #
    try:
       loop.run_until_complete(run())
    except KeyboardInterrupt:
       print('User has stopped run() from executing.')

Here, the output would be module3.py died, even if module3.py had successful startup (and, in turn, the flag value was changed to True).

I am aware that I likely do not need to have the flag in a separate module, but I found it to be quite convenient. I would appreciate any advice as to how to solve this problem. Thank you!

Augustus
  • 31
  • 7
  • add a while loop, check flag, if it's not set, sleep. If you've slept for long enough, die. – Mahrkeenerh Nov 12 '21 at 22:34
  • Processes each run in their own memory space, so you can't simply have a global flag that all can access. However you can use a [`multiprocessing.managers.SyncManager`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager) to share certain types of objects among two or more of them. The manager will keep the current value of the object synchronized among the processes, but the values have to accessed indirectly through "proxies". – martineau Nov 12 '21 at 22:58
  • How is this related to tkinter? – PCM Nov 13 '21 at 05:17
  • @Mahrkeenerh that did not work unfortunately, and I got an infinite loop as a result. Thank you for your input though. – Augustus Nov 17 '21 at 19:44
  • @martineau Thank you so much for your feedback! I was not aware that I could not do that in Python. Would it be possible then to have a synchronized list of booleans, and then have module1.py check this list of booleans for any change in value? – Augustus Nov 17 '21 at 19:48
  • @PCM The project this is for utilizes Tkinter, although I did not explicitly state that in the post. Apologies. – Augustus Nov 17 '21 at 19:48
  • Augustus: Yes, you could create a managed list of boolean (or whatever) values that was shared and have another processes (including the main one) check it periodically for changes. Alternatively you could have single `Value` that was shared through one. – martineau Nov 17 '21 at 21:10
  • @martineau Thank you very much for your help. I do have another question though if you don't mind, and that is how do I actually go about sharing this synced variable between modules whilst avoiding a circular import? Intuitively, I would think to just pass in the variable as an argument in subprocess but I'm unsure of how to do that... – Augustus Nov 17 '21 at 22:59
  • Augustus: I'm not sure how or if it's possible to do it with the `subprocess.Popen` class and having only ever seen it done with the [`multiprocessing.Process`](https://docs.python.org/3/library/multiprocessing.html#the-process-class) class. With the latter, you can pass managed things as arguments when creating instances of the class. – martineau Nov 18 '21 at 00:06
  • Augustus: See [What is the difference between multiprocessing and subprocess?](https://stackoverflow.com/questions/13606867/what-is-the-difference-between-multiprocessing-and-subprocess) Since all your tasks are written in python you should be able to easily switch to `multiprocessing`. – martineau Nov 18 '21 at 00:15
  • Augustus: Actually you *may* be able to use them together: see [From subprocess.Popen to multiprocessing](https://stackoverflow.com/questions/39943281/from-subprocess-popen-to-multiprocessing). – martineau Nov 18 '21 at 00:26
  • @martineau A bit late but I found a cool workaround :) I just use Popen.wait() as a flag. If it throws a timeout exception then `module3.py` must have started successfully. Else, `module3.py` must have died prematurely. Thank you very much for your help! – Augustus Nov 23 '21 at 21:39
  • 1
    @Augustus: Cool and clever! Glad to hear you found a such a (relatively) simple solution…and thanks for letting me know. Note that you can answer your own questions here (and even accept them)—which is an even better way of disseminating the knowledge. – martineau Nov 23 '21 at 21:48

1 Answers1

1

Sharing data between processes via subprocessing was a pain, so I opted to use Popen.wait() as an indicator for whether or not my child process successfully initialized.

Augustus
  • 31
  • 7