Skip to content
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

bpo-31938: Convert selectmodule.c to Argument Clinic #4265

Merged
merged 19 commits into from
Jun 30, 2018

Conversation

taleinat
Copy link
Contributor

@taleinat taleinat commented Nov 3, 2017

This is a complete Argument Clinic conversion of Modules/selectmodule.c.

The initial commit on this branch is the mostly fixed application of the latest patch on bpo-20182. Later changes were each done in a separate commit to facilitate review.

Minor note: Some of the early commits on the branch don't compile successfully.

https://bugs.python.org/issue31938

Not in working condition, things need to be moved around to work with
generation of AC code in a separate file.
This is required to be able to include the generated AC code from
clinic/selectmodule.c.h, which must appear before these definitions
but after all typedefs and type declarations.
The converter was copied from Modules/posixmodule.c, but changed to
check whether fd == -1 rather than fd < 0 as an error condition from
calling PyOjbect_AsFileDescriptor().

Note: AC output in Modules/clinic/selectmodule.c.h was manually
modified in this commit to get the build working. This may be due to
an AC bug.
Note: AC output in Modules/clinic/selectmodule.c.h was manually
modified in this commit to get the build working. This may be due to
an AC bug.
Also change the doc-string of epoll.poll:
* use AC's per-argument docs to describe the arguments
* revert an addition describing the return value

Note: AC output in Modules/clinic/selectmodule.c.h was manually
modified in this commit to get the build working. This may be due to
an AC bug.

fd: fildes
either an integer, or an object with a fileno() method returning an int
eventmask: object(converter="ushort_converter", type="unsigned short", c_default="POLLIN | POLLOUT | POLLPRI") = POLLIN | POLLOUT | POLLPRI
Copy link
Member

Choose a reason for hiding this comment

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

Good opportunity to register the ushort converter.

Copy link
Member

Choose a reason for hiding this comment

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

Please keep flags in this order: POLLIN | POLLPRI | POLLOUT. It seems like POLLPRI is more related to POLLIN, than POLLOUT. See https://bugs.python.org/issue30844 :-)

if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL;
}

if (timeout_obj != NULL && timeout_obj != Py_None) {
Copy link
Member

Choose a reason for hiding this comment

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

timeout_obj can't be NULL.

if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL;
}

/* Check values for timeout */
if (timeout_obj == NULL || timeout_obj == Py_None) {
Copy link
Member

Choose a reason for hiding this comment

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

timeout_obj can't be NULL.

@classmethod
select.epoll.__new__

sizehint: int(c_default="FD_SETSIZE - 1") = -1
Copy link
Member

Choose a reason for hiding this comment

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

Negative sizehint is an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should it be an error? The tests only check that -2 should raise ValueError, not -1. The existing doc-string mentions that -1 should mean the default size, though it currently doesn't.

Perhaps we should fix the code to special-case -1? Currently the only way to get the default is to not specify sizehint, but that's one type of inconsistency AC aims to remove.

Copy link
Member

Choose a reason for hiding this comment

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

See the check at line 1304. The current code treats -1 as invalid value. Argument Clinic conversion shouldn't change semantic (too much). If there is a bug in the current code, fix it in a separate issue, so that the fix can be backported.

Copy link
Contributor Author

@taleinat taleinat Nov 4, 2017

Choose a reason for hiding this comment

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

This may not actually matter much. From the docs: "sizehint and flags are deprecated and completely ignored." Looking at the code, this is true for flags, but sizehint is actually passed to epoll_create if HAVE_EPOLL_CREATE1 isn't defined.

So there are two problems right now:

  1. The current doc-string is simply wrong: "sizehint must be a positive integer or -1 for the default size"
  2. Since -1 is actually invalid, there's currently no value that can be used in the doc-string to describe the default for sizehint, which is slightly bad for AC conversion.

Fixing the doc-string to describe the actual behavior seems like the way to go. I'm just not sure what that is regarding sizehint: is it actually ignored? I.e. are the docs wrong on this?

I'm really unsure what to do regarding the default value for sizehint shown in the doc-string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@serhiy-storchaka Could you help decide how to resolve this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm summarizing in hope to move forward with this.

The default for sizehint should ideally be FD_SETSIZE - 1, but FD_SETSIZE isn't exposed at the Python level. The current behavior sets sizehint to FD_SETSIZE - 1 if not supplied, but that's a type of inconsistency that AC aims to remove.

-1 is an invalid value, therefore it should probably not be used as a default value since that would change the existing semantics too much.

The docs say that sizehint is deprecated and ignored, in which case this discussion would be moot. However in the code sizehint is still passed to epoll_create() if epoll_create1() is not available:

#ifdef HAVE_EPOLL_CREATE1
        self->epfd = epoll_create1(EPOLL_CLOEXEC);
#else
        self->epfd = epoll_create(sizehint);
#endif

Copy link
Member

Choose a reason for hiding this comment

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

-1 was valid before 2fb9ae9 (but 0 was not valid). I'm not sure this change is intentional. It is better to open a separate issue for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@serhiy-storchaka, I'll open a new issue. In the meantime, what do we do with the default value of sizehint in this branch?

Copy link
Member

Choose a reason for hiding this comment

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

Depending on the results of that issue. Likely -1 will be the default value.

Copy link
Contributor Author

@taleinat taleinat Jan 16, 2018

Choose a reason for hiding this comment

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

Created bpo-32568.


return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_DEL, pfd, 0);
}
timeout as timeout_obj: object(c_default="Py_None") = -1.0
Copy link
Member

Choose a reason for hiding this comment

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

Why not use None as a default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The doc-string mentions, regarding timeout, "-1 makes poll wait indefinitely" but doesn't mention None, though None actually does the same. Given this, I thought documenting the default as -1 was clearer.

Perhaps we should improve the doc-string, mentioning that either None or -1 mean "wait indefinitely", and then change the default to None?

Copy link
Member

Choose a reason for hiding this comment

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

Do what looks better to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll improve the doc-string and change the default to None.

@@ -2158,8 +2087,8 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
ptimeoutspec = &timeoutspec;
}

if (ch != NULL && ch != Py_None) {
seq = PySequence_Fast(ch, "changelist is not iterable");
if (changelist != NULL && changelist != Py_None) {
Copy link
Member

Choose a reason for hiding this comment

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

otimeout and changelist can't be NULL.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

Thank you for this nice PR. While I may have other comments later, and @serhiy-storchaka already added comments, I like how the new code looks like ;-)

@@ -702,39 +748,6 @@ poll_dealloc(pollObject *self)
PyObject_Del(self);
}

static PyTypeObject poll_Type = {
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to not move this code, to group all code related to poll at the same place, as done currently.

Copy link
Contributor Author

@taleinat taleinat Nov 4, 2017

Choose a reason for hiding this comment

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

Currently we must either move all method and type definitions towards the end of the file, or move all typedefs and type declarations to the beginning. This is because #include "clinic/selectmodule.c.h" must come between them.

On bpo-20180 (msg304976), regarding itertools, @serhiy-storchaka mentioned preference for moving the method and type definitions to the end rather than the alternative. Therefore I did the same here.

Copy link
Member

Choose a reason for hiding this comment

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

This is not a preference, this is just an option. Since methods lists and type object initializers could be generated by Argument Clinic in future, I think it is easier to move them.


fd: fildes
either an integer, or an object with a fileno() method returning an int
eventmask: object(converter="ushort_converter", type="unsigned short", c_default="POLLIN | POLLOUT | POLLPRI") = POLLIN | POLLOUT | POLLPRI
Copy link
Member

Choose a reason for hiding this comment

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

Please keep flags in this order: POLLIN | POLLPRI | POLLOUT. It seems like POLLPRI is more related to POLLIN, than POLLOUT. See https://bugs.python.org/issue30844 :-)

int.\n\
events -- an optional bitmask describing the type of events to check for");
/*[clinic input]
select.poll.register
Copy link
Member

Choose a reason for hiding this comment

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

Please use "-> NoneType". Even if currently it seems like it has no effect, it will be used in signature once https://bugs.python.org/issue31939 will be implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That syntax is for defining AC return converters, not return type annotations, so it could have unwanted effects.

Specifically, the Argument Clinic How-To currently states:

(There’s also an experimental NoneType converter, which lets you return Py_None on success or NULL on failure, without having to increment the reference count on Py_None. I’m not sure it adds enough clarity to be worth using.)

static PyObject*
devpoll_fileno(devpollObject *self)
/*[clinic input]
select.devpoll.fileno
Copy link
Member

Choose a reason for hiding this comment

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

Please try to use "-> int" return converter. Same comment for other fileno() methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regarding devpoll.fileno(), due to the use of devpoll_err_closed() internally, using -> int or -> long actually complicates the code. The same goes for epoll.fileno() and kqueue.fileno() as well.

Copy link
Member

Choose a reason for hiding this comment

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

This will complicate the code. select_devpoll_fileno_impl would be need to convert NULL returned by devpoll_err_closed() to -1. I think it is better to keep the current code and add the converter in a separate issue if you with.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

This PR is just giant. I would suggest to split it into multiple smaller PRs. For example, why not starting by converting only select()?

int fd;
int *pointer = (int *)p;
fd = PyObject_AsFileDescriptor(o);
if (fd == -1)
Copy link
Member

Choose a reason for hiding this comment

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

PEP 7: add { ... }.

{
int fd;
int *pointer = (int *)p;
fd = PyObject_AsFileDescriptor(o);
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: you can write "int fd = ...;" (single line)


*** IMPORTANT NOTICE ***
On Windows, only sockets are supported; on Unix, all file
descriptors can be used.
Copy link
Member

Choose a reason for hiding this comment

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

used => supported or acceptd.

Wait until one or more file descriptors are ready for some kind of I/O.

The first three arguments are sequences of file descriptors to be waited for:
rlist -- wait until ready for reading
Copy link
Member

Choose a reason for hiding this comment

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

I would remove "wait until" since select is non-blocking when using a timeout of 0.

The first three arguments are sequences of file descriptors to be waited for:
rlist -- wait until ready for reading
wlist -- wait until ready for writing
xlist -- wait for an ``exceptional condition''
Copy link
Member

Choose a reason for hiding this comment

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

the prevent encoding issues on legacy terminals, I suggest to use more common and ASCII quote: '...'

Copy link
Member

Choose a reason for hiding this comment

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

xlist -- wait for an ``exceptional condition''
If only one kind of condition is required, pass [] for the other lists.
A file descriptor is either a socket or file object, or a small integer
gotten from a fileno() method call on one of those.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if it's worth it to mention it, but on Windows, technically select() expects a list of socket handles, not file descriptors. On Windows, socket handles and file descriptors are two distinct namespaces.

efdlist = set2list(&efdset, efd2obj);
rlist = set2list(&ifdset, rfd2obj);
wlist = set2list(&ofdset, wfd2obj);
xlist = set2list(&efdset, efd2obj);
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer "elist" to be consisten with "efdset" and "efd2obj".

@taleinat
Copy link
Contributor Author

This PR is just giant. I would suggest to split it into multiple smaller PRs. For example, why not starting by converting only select()?

I can understand why you suggest this. I'm discouraged by the amount of work required in addition to that already invested here. I'm further discouraged since I've done exactly what you suggest for the AC conversion of itertools, beginning with converting only groupby(), but that has been completely ignored so far (PR #4170).

@vstinner
Copy link
Member

I've done exactly what you suggest for the AC conversion of itertools, beginning with converting only groupby(), but that has been completely ignored so far (PR #4170).

I like AC and want to convert everything to AC, but I have limited bandwidth to review such AC. Please don't hesitate to ping me frequently until I reviewed all of them :-)

@serhiy-storchaka
Copy link
Member

Making all changes with a single PR LGTM. This is unavoidable when convert to AC such module. Let me to recall where we stopped last time. I guess there were just minor problems which should be resolved.

@taleinat
Copy link
Contributor Author

@serhiy-storchaka

Let me to recall where we stopped last time.

I've got a patch ready which addresses nearly all of those issues. If you're interested I'll get it done and push it to this branch. I suggest that you wait until that's done before reviewing again.

@taleinat
Copy link
Contributor Author

@vstinner

I like AC and want to convert everything to AC, but I have limited bandwidth to review such AC. Please don't hesitate to ping me frequently until I reviewed all of them :-)

I greatly appreciate your bandwidth, it's obvious you spend a lot of time and effort to address so many different issues! I realize that AC is lower priority than most things and accept that. I'll work on moving my AC patches one at a time, in hopes of getting things moving in smaller steps will require less ongoing commitment from other core devs.

@taleinat
Copy link
Contributor Author

@vstinner, AFAICT all of your comments in the last batch are regarding code and comments which I mostly moved around rather than actually wrote or edited. I'd rather make such changes after the AC conversion if you don't mind. I'd gladly collect your comments here and prepare another PR with just those improvements, after this is done with.

@taleinat
Copy link
Contributor Author

@serhiy-storchaka, I've fixed all of the outstanding issues brought up here that it was clear should be fixed. I think this is ready for another review!

I've left the epoll sizehint behavior as-is, i.e. this patch doesn't change the existing behavior, questionable though it may be. We are addressing that on the dedicated issue bpo-32568.

I've also not addressed the comments from @vstinner's latest review, since they are all about things I haven't actually changed here.

@@ -875,13 +896,20 @@ devpoll_unregister(devpollObject *self, PyObject *o)
Py_RETURN_NONE;
}

PyDoc_STRVAR(devpoll_poll_doc,
"poll( [timeout] ) -> list of (fd, event) 2-tuples\n\n\
Copy link
Member

Choose a reason for hiding this comment

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

The hint about the structure of the return value is lost. Same for epoll.poll.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! I'd addressed this for poll.poll() but not devpoll.poll() and epoll.poll(). Fixed!

The first three arguments are sequences of file descriptors to be waited for:
rlist -- wait until ready for reading
wlist -- wait until ready for writing
xlist -- wait for an ``exceptional condition''
Copy link
Member

Choose a reason for hiding this comment

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

/*[clinic input]
select.devpoll.poll

timeout as timeout_obj: object = None
Copy link
Member

Choose a reason for hiding this comment

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

Should be positional-only.

@taleinat
Copy link
Contributor Author

@serhiy-storchaka, I think this is ready for another review!

I've addressed your latest two comments and fixed a couple of bugs related to select.kqueue (now all tests are passing).

# Conflicts:
#	Modules/selectmodule.c
@taleinat taleinat merged commit 6dc57e2 into python:master Jun 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants