Skip to content

tools/pyboard.py: ProcessToSerial cross-platform fix #8435

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

vikmal-sw
Copy link

Using ProcessToSerial class from pyboard.py on Windows was not possible due to some Unix-only code (for example, select.poll or selectors.select()). A solution to get ProcessToSerial working on Windows is by creating a thread that constantly reads from the subp.stdout and completes a queue. Then, the read() method will get the characters from the queue. This solution also works on Unix platforms, but I have implemented it only for Windows platforms, leaving the original code for Unix platforms.

Using ProcessToSerial class from pyboard.py on Windows was not possible due to some Unix-only code (for example, select.poll or selectors.select()). A solution to get ProcessToSerial working on Windows is by creating a thread that constantly reads from the subp.stdout and completes a queue. Then, the read() method will get the characters from the queue. This solution also works on Unix platforms, but I have implemented it only for Windows platforms, leaving the original code for Unix platforms.
@dpgeorge dpgeorge added the tools Relates to tools/ directory in source, or other tooling label Mar 24, 2022
@codecov-commenter
Copy link

Codecov Report

Merging #8435 (1ce0d10) into master (afceb56) will not change coverage.
The diff coverage is n/a.

@@           Coverage Diff           @@
##           master    #8435   +/-   ##
=======================================
  Coverage   98.25%   98.25%           
=======================================
  Files         154      154           
  Lines       20284    20284           
=======================================
  Hits        19931    19931           
  Misses        353      353           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update afceb56...1ce0d10. Read the comment docs.

tools/pyboard.py Outdated
continue
else:
q.put(data)
data_prec = data

class ProcessToSerial:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be much cleaner to create separate classes, such as: ProcessToSerialPosix and ProcessToSerialThreaded. Then some code like:

import select
if hasattr(select, "poll"):
    ProcessToSerial = ProcessToSerialPosix
else:
    ProcessToSerial = ProcessToSerialThreading

@dpgeorge
Copy link
Member

Thanks for the contribution!

It's also possible to use socket.socketpair() on Windows (and Linux) and use that instead of a Queue to communicate between the threads. The benefit of a socket pair is that you can use select.select(...) on it to efficiently wait for data.

@vikmal-sw vikmal-sw marked this pull request as draft March 24, 2022 13:32
@vikmal-sw vikmal-sw marked this pull request as ready for review March 24, 2022 13:34
@vikmal-sw vikmal-sw marked this pull request as draft March 24, 2022 13:34
In order to make the code cleaner, there are 2 separate classes instead of ProcessToSerial :
- ProcessToSerialPosix which uses select module to check if any data available on the subprocess stdout (working well on Unix)
- ProcessToSerialThreading which uses a thread that completes a queue with data from the subprocess stdout (working well on Windows as an alternative to select)
The choice is made by verifying if select has the attribute "poll" on the host platform
@vikmal-sw
Copy link
Author

Thank you very much for your review and answer dpgeorge !
I have created 2 separate classes as you suggested : ProcessToSerialPosix and ProcessToSerialThreading
I have also investigated the possibility of using socket.socketpair(). It is a great idea, but I found that it is supported on Windows only by Python version 3.5 or higher which I am afraid introduces Python 3.5 dependency. What do you think ?
Thank you very much in advance !
Best regards,
Victor Malai

@vikmal-sw vikmal-sw marked this pull request as ready for review March 24, 2022 13:55
tools/pyboard.py Outdated
if hasattr(select, "poll"):
self.serial = ProcessToSerialPosix(device[len("exec:") :])
else:
self.serial = ProcessToSerialThreading(device[len("exec:") :])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ProcessToSerial class is really part of the public API of this file, so I suggest to put this code in the global scope, so that ProcessToSerial is defined in the global scope. Then the code here doesn't need to change at all.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse me, I don't have much experience with Python. If I keep the ProcessToSerial class, how and where I could declare ProcessToSerialPosix and ProcessToSerialThreading ? I appreciate very much your help and indications.
Thank you in advance.
Best regards,
Victor

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class ProcessToSerialPosix:
    ...

class ProcessToSerialThreading:
    ...

if hasattr(select, "poll"):
    ProcessToSerial = ProcessToSerialPosix
else:
    ProcessToSerial = ProcessToSerialThreading

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for your time and help ! I didn't know you can do that in Python. I made a commit with these changes.

@dpgeorge
Copy link
Member

I have also investigated the possibility of using socket.socketpair(). It is a great idea, but I found that it is supported on Windows only by Python version 3.5 or higher which I am afraid introduces Python 3.5 dependency. What do you think ?

OK, in that case let's stick with Queue.

@@ -153,13 +153,72 @@ def inWaiting(self):
else:
return n_waiting

# A thread that constantly reads from subp stdout and puts data into a queue (FIFO list)
# When tested, it reads double '\r' which messes up the further code, so if double '\r' detected, skip one
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you understand why it reads double \r?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried to investigate the reason, but unfortunately I am still not able to understand why it reads double \r

@vikmal-sw vikmal-sw marked this pull request as draft April 4, 2022 09:04
Considering ProcessToSerial is a part of the public API of this file, it is defined in the global scope. This leaves the Pyboard class code unchanged.
@vikmal-sw vikmal-sw marked this pull request as ready for review April 4, 2022 09:17
tannewt added a commit to tannewt/circuitpython that referenced this pull request Sep 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tools Relates to tools/ directory in source, or other tooling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants