You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current code of the fcntl module is a little mess. This was caused particularly by the nature of the underlying C API (fcntl() and ioctl() take an argument as a C int or a pointer to a structure, and the type of the argument, as well as the size of the structure is not explicitly specified, but depends on opcode and is platform depending), and particularly by the decades of incremental changes.
At Python level, fcntl() and ioctl() take an optional argument which can be an integer, a string, or a mutable or immutable bytes-like object. The result can be an integer or a bytes object. The input mutable bytes-like object can be modified in-place in ioctl(). Issues, differences and inconsistencies between fcntl() and ioctl():
ioctl() first try to interpret the argument as a mutable bytes-like object, then as a string or immutable bytes-like object, then as an integer. fcntl() does the same except trying a mutable bytes-like object. Raising and silencing exceptions is not particularly efficient, and silencing arbitrary exceptions is not good.
fcntl() and ioctl() accept string argument. This looks like an unintentional side effect in Python 2 which was not fixed in Python 3. I do not know any operation code which takes a string argument -- they take binary structures. Even if there is an operation which takes a char array, most likely it should be encoded using filesystem encoding instead of UTF-8, so bytes is more preferable than str. String argument should be deprecated.
ioctl() makes the temporary buffer null-terminated, but fcntl() does not.
ioctl() has the mutate_arg parameter, but fcntl() does not, even if some operations mutate argument.
ioctl() copies the content of the mutable bytes-like object to a temporary buffer if mutate_arg is true and it fits in a buffer, and then copies it back. It was needed when the parsing code used "w#", because the object could be resized after releasing the GIL. And it was wrong, because copying back will not work after resizing. But now "w*" is used ("w#" no longer supported), so no copying to temporary buffer is needed.
ioctl() does not release the GIL when call the C ioctl() if the mutable bytes-like object does not fit in the temporary buffer. It can, for reasons described above.
ioctl() accepts opcode as unsigned intunsigned long, fcntl() accepts only int. fcntl() accepts the integer argument as unsigned int, ioctl() accepts only int. Using an unsigned format makes the code more error-prone (Deprecate accepting out of range values for unsigned integers in PyArg_Parse #132629 should mitigate this), but makes it more usable if some opcode or value constants in C has the MSB set. I think that both function should accept unsigned int (unsigned long?) for both arguments.
fcntl() automatically retries on EINTR, ioctl() raises an OSError.
The current code of the
fcntlmodule is a little mess. This was caused particularly by the nature of the underlying C API (fcntl()andioctl()take an argument as a C int or a pointer to a structure, and the type of the argument, as well as the size of the structure is not explicitly specified, but depends on opcode and is platform depending), and particularly by the decades of incremental changes.At Python level,
fcntl()andioctl()take an optional argument which can be an integer, a string, or a mutable or immutable bytes-like object. The result can be an integer or a bytes object. The input mutable bytes-like object can be modified in-place inioctl(). Issues, differences and inconsistencies betweenfcntl()andioctl():ioctl()first try to interpret the argument as a mutable bytes-like object, then as a string or immutable bytes-like object, then as an integer.fcntl()does the same except trying a mutable bytes-like object. Raising and silencing exceptions is not particularly efficient, and silencing arbitrary exceptions is not good.fcntl()andioctl()accept string argument. This looks like an unintentional side effect in Python 2 which was not fixed in Python 3. I do not know any operation code which takes a string argument -- they take binary structures. Even if there is an operation which takes a char array, most likely it should be encoded using filesystem encoding instead of UTF-8, sobytesis more preferable thanstr. String argument should be deprecated.ioctl()makes the temporary buffer null-terminated, butfcntl()does not.ioctl()has themutate_argparameter, butfcntl()does not, even if some operations mutate argument.ioctl()copies the content of the mutable bytes-like object to a temporary buffer ifmutate_argis true and it fits in a buffer, and then copies it back. It was needed when the parsing code used "w#", because the object could be resized after releasing the GIL. And it was wrong, because copying back will not work after resizing. But now "w*" is used ("w#" no longer supported), so no copying to temporary buffer is needed.ioctl()does not release the GIL when call the Cioctl()if the mutable bytes-like object does not fit in the temporary buffer. It can, for reasons described above.ioctl()accepts opcode asunsigned intunsigned long,fcntl()accepts onlyint.fcntl()accepts the integer argument asunsigned int,ioctl()accepts onlyint. Using an unsigned format makes the code more error-prone (Deprecate accepting out of range values for unsigned integers in PyArg_Parse #132629 should mitigate this), but makes it more usable if some opcode or value constants in C has the MSB set. I think that both function should acceptunsigned int(unsigned long?) for both arguments.fcntl()automatically retries on EINTR,ioctl()raises an OSError.Linked PRs