MrYsLab / pymata-aio

This is the second generation PyMata client.
https://github.com/MrYsLab/pymata-aio/wiki
GNU Affero General Public License v3.0
155 stars 51 forks source link

PymataCore.shutdown shouldn't call sys.exit(0) #66

Closed alberand closed 6 years ago

alberand commented 6 years ago

Hi, in the examples you use function board.shutdown() (PymataCore.shutdown) which shutdowns a board and calls sys.exit(0) which is not right. The function is doing something that is out of its scope. It should shutdown the board not the application.

As an example, I am trying to make an interactive shell (ipython + pymata) to work with a board. When I call the shutdown function it tries to close the shell. This is also applies to other application which tries to call the shutdown.

What do you think?

MrYsLab commented 6 years ago

Shutdown is used both for internal errors that are considered fatal and unrecoverable, and for use by the application itself. If you are requesting that the user call of the shutdown method have an option to not to call exit(), I would consider making the change. Fatal internal errors would still however, exit..

Would having an option in shutdown for the user call to exit or not solve your problem?

alberand commented 6 years ago

I mean that it is not the thing which library should do (exit the application). The library shouldn't decide when to end application or not. If the fatal internal error occurs it should be thrown to upper layer and safely terminate the execution.

I don't ask you to remove this call, but I think it is more expected behavior. Why is there even a need in exiting if the fatal error occur?

MrYsLab commented 6 years ago

Normally I would agree, but having another computer in the mix (an Arduino) that does not throw exceptions (Firmata is very limited in that regard), and that has an inconsistent hardware reset scheme (between models of Arduino), I have found that this is the simplest solution when communication with the Arduino is lost. There is no easy or reliable way to get both pymata and the Arduino back in communications sync when something goes wrong, and in some instances even after an orderly shutdown. That is why this "extreme" measure is taken.

MrYsLab commented 6 years ago

I am closing this issue, but if you would like to continue the conversation or have any additional comments, please feel free to add them to this thread and I will see them.

chelsell commented 5 years ago

@MrYsLab what, then, is the way to handle the following case:

My application uses PyMata to manage an arduino. At a certain point, it doesn't need the arduino anymore, but it does need to do some time consuming processing. However, I don't want this processing step to block the serial port because I want to be able to run a new instance of the application using the same arduino.

I'll tell you the solution I came up with, and you can tell me if it's too unconscionable a hack:

try:
   board.shutdown()
except:
   pass
try:
   board.core.serial_port.my_serial.close()
except:
   pass

Another thing I experimented with was spawning the processing step as a new process, relying on sys.exit to clear the serial port for me. This could have worked if I were on a Unix platform, because I would be able to fork the new process. But on Windows doing that kind of thing is apparently much stickier.

MrYsLab commented 5 years ago

@chelsell If the time consuming process is a blocking process (non-asyncio) integrating it within an asyncio process is not easily done. If you want to launch it from your asyncio process, you can look at lines 213 through 222 in s2aio where the Scratch program is launched within an asyncio method. It works for both windows and linux/mac.

You can also look at subprocess with asyncio, but I have not tried this approach.