-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
#30014: refactor poll-related classes #1035
Conversation
…poll() share the same methods for register(), unregister(), close() and select()
Lib/selectors.py
Outdated
"""Poll-based selector.""" | ||
def __init__(self): | ||
super().__init__() | ||
self._poller = self._poller() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks strange: I assume that self._poller is the poller class (e.g. select, poll, etc).
It's used here but not defined in this base class: maybe make it an abstract property to force its definition?
Also, assigning
self._poller = self._poller() is very confusing: you're reusing the same member for the instance and the class.
Maybe use poller_cls, or poller_type for the type of the poller, to avoid confusion with the instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the idea is to have a _poller
attribute which can be referenced by all 3 classes. Actually I think it'd be cool to do the same also for select and kqueue.
And yes, it's probably better to also have _poller_cls
.
I don't have a strong opinion about abstract properties but it's up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would you feel about having a _selector
attribute for all classes (also kqueue and select)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Lib/selectors.py
Outdated
poller_events |= self._EVENT_WRITE | ||
try: | ||
self._poller.register(key.fd, poller_events) | ||
except BaseException: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think catching BaseException is discouraged, shouldn't it be Exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think it should; I think I copied it from the previous code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Lib/selectors.py
Outdated
key = super().unregister(fileobj) | ||
try: | ||
self._poller.unregister(key.fd) | ||
except OSError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why catch OSError here and BaseException in register()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied it from the previous code:
Line 423 in 2e576f5
except OSError: |
...although the original code did that for epoll only wheres with this it's applied to all 3 selectors. Not sure if it matters. Probably not.
In general I think it would make sense to always catch OSError (also in kqueue).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, let's catch OSError everywhere.
Lib/selectors.py
Outdated
self._devpoll = select.devpoll() | ||
_poller = select.devpoll | ||
_EVENT_READ = select.POLLIN | ||
_EVENT_WRITE = select.POLLOUT | ||
|
||
def fileno(self): | ||
return self._devpoll.fileno() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't work, self._devpoll has been replaced by self._poller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Fixed.
Lib/selectors.py
Outdated
ready.append((key, events & key.events)) | ||
return ready | ||
def close(self): | ||
if hasattr(self._poller, "close"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing such a hasattr() is a bit naughty: why not instead override close() in EpollSelector and DevPollSelector to close the underlying FD?
If you want to maximize code reuse, maybe we could add a _FDBasedSelector - common to al implementations using a file descriptor - in which you could define close() and fileno()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine with me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I overrode close() in EpollSelector and DevPollSelector to close the underlying FD as per your suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, didn't see this message.
With the proposed changes, sounds great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, didn't see this message.
No problem. ;-)
OK, I think it's ready.
Lib/selectors.py
Outdated
"""Poll-based selector.""" | ||
def __init__(self): | ||
super().__init__() | ||
self._poller = self._poller() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Lib/selectors.py
Outdated
poller_events |= self._EVENT_WRITE | ||
try: | ||
self._poller.register(key.fd, poller_events) | ||
except BaseException: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Lib/selectors.py
Outdated
ready.append((key, events & key.events)) | ||
return ready | ||
def close(self): | ||
if hasattr(self._poller, "close"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I overrode close() in EpollSelector and DevPollSelector to close the underlying FD as per your suggestion.
Lib/selectors.py
Outdated
self._devpoll = select.devpoll() | ||
_poller = select.devpoll | ||
_EVENT_READ = select.POLLIN | ||
_EVENT_WRITE = select.POLLOUT | ||
|
||
def fileno(self): | ||
return self._devpoll.fileno() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
Cool. Merged. I'll submit a PR for a faster |
Congrats giampaolo for fixing that old issue :-) I hope that it will make
further selectors enhancements simpler.
|
Thanks Victor. ;) |
Refactor poll-related classes so that poll(), epoll() and devpoll() share the same methods for register(), unregister(), close() and select().
This was inspired by http://bugs.python.org/issue30014 and serves as a strategy to avoid duplicating the implementation of modify() method.
UPDATE 2017-05-20: also use a single
self._poller
attribute for all classes instead of havingself._poll
,self._kqueue
etc..@Haypo