]> granicus.if.org Git - python/commitdiff
Issue 20152, 22821: Port the fcntl module to Argument Clinic.
authorBrett Cannon <brett@python.org>
Mon, 10 Nov 2014 01:22:01 +0000 (20:22 -0500)
committerBrett Cannon <brett@python.org>
Mon, 10 Nov 2014 01:22:01 +0000 (20:22 -0500)
Along the way, fix an argumrnt to fcntl.fcntl to be an int instead of
a long.

Thanks to Serhiy Storchaka for reviewing my Clinic patch and for
writing the patch to fix the long/int issue.

Modules/clinic/fcntlmodule.c.h [new file with mode: 0644]
Modules/fcntlmodule.c

diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h
new file mode 100644 (file)
index 0000000..377e55d
--- /dev/null
@@ -0,0 +1,188 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(fcntl_fcntl__doc__,
+"fcntl($module, fd, code, arg=None, /)\n"
+"--\n"
+"\n"
+"Perform the operation `code` on file descriptor fd.\n"
+"\n"
+"The values used for `code` are operating system dependent, and are available\n"
+"as constants in the fcntl module, using the same names as used in\n"
+"the relevant C header files.  The argument arg is optional, and\n"
+"defaults to 0; it may be an int or a string.  If arg is given as a string,\n"
+"the return value of fcntl is a string of that length, containing the\n"
+"resulting value put in the arg buffer by the operating system.  The length\n"
+"of the arg string is not allowed to exceed 1024 bytes.  If the arg given\n"
+"is an integer or if none is specified, the result value is an integer\n"
+"corresponding to the return value of the fcntl call in the C code.");
+
+#define FCNTL_FCNTL_METHODDEF    \
+    {"fcntl", (PyCFunction)fcntl_fcntl, METH_VARARGS, fcntl_fcntl__doc__},
+
+static PyObject *
+fcntl_fcntl_impl(PyModuleDef *module, int fd, int code, PyObject *arg);
+
+static PyObject *
+fcntl_fcntl(PyModuleDef *module, PyObject *args)
+{
+    PyObject *return_value = NULL;
+    int fd;
+    int code;
+    PyObject *arg = NULL;
+
+    if (!PyArg_ParseTuple(args,
+        "O&i|O:fcntl",
+        conv_descriptor, &fd, &code, &arg))
+        goto exit;
+    return_value = fcntl_fcntl_impl(module, fd, code, arg);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(fcntl_ioctl__doc__,
+"ioctl($module, fd, op, arg=None, mutate_flag=True, /)\n"
+"--\n"
+"\n"
+"Perform the operation op on file descriptor fd.\n"
+"\n"
+"The values used for op are operating system dependent, and are available as\n"
+"constants in the fcntl or termios library modules, using the same names as\n"
+"used in the relevant C header files.\n"
+"\n"
+"The argument `arg` is optional, and defaults to 0; it may be an int or a\n"
+"buffer containing character data (most likely a string or an array).\n"
+"\n"
+"If the argument is a mutable buffer (such as an array) and if the\n"
+"mutate_flag argument (which is only allowed in this case) is true then the\n"
+"buffer is (in effect) passed to the operating system and changes made by\n"
+"the OS will be reflected in the contents of the buffer after the call has\n"
+"returned.  The return value is the integer returned by the ioctl system\n"
+"call.\n"
+"\n"
+"If the argument is a mutable buffer and the mutable_flag argument is not\n"
+"passed or is false, the behavior is as if a string had been passed.  This\n"
+"behavior will change in future releases of Python.\n"
+"\n"
+"If the argument is an immutable buffer (most likely a string) then a copy\n"
+"of the buffer is passed to the operating system and the return value is a\n"
+"string of the same length containing whatever the operating system put in\n"
+"the buffer.  The length of the arg buffer in this case is not allowed to\n"
+"exceed 1024 bytes.\n"
+"\n"
+"If the arg given is an integer or if none is specified, the result value is\n"
+"an integer corresponding to the return value of the ioctl call in the C\n"
+"code.");
+
+#define FCNTL_IOCTL_METHODDEF    \
+    {"ioctl", (PyCFunction)fcntl_ioctl, METH_VARARGS, fcntl_ioctl__doc__},
+
+static PyObject *
+fcntl_ioctl_impl(PyModuleDef *module, int fd, unsigned int code, PyObject *ob_arg, int mutate_arg);
+
+static PyObject *
+fcntl_ioctl(PyModuleDef *module, PyObject *args)
+{
+    PyObject *return_value = NULL;
+    int fd;
+    unsigned int code;
+    PyObject *ob_arg = NULL;
+    int mutate_arg = 1;
+
+    if (!PyArg_ParseTuple(args,
+        "O&I|Op:ioctl",
+        conv_descriptor, &fd, &code, &ob_arg, &mutate_arg))
+        goto exit;
+    return_value = fcntl_ioctl_impl(module, fd, code, ob_arg, mutate_arg);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(fcntl_flock__doc__,
+"flock($module, fd, code, /)\n"
+"--\n"
+"\n"
+"Perform the lock operation op on file descriptor fd.\n"
+"\n"
+"See the Unix manual page for flock(2) for details (On some systems, this\n"
+"function is emulated using fcntl()).");
+
+#define FCNTL_FLOCK_METHODDEF    \
+    {"flock", (PyCFunction)fcntl_flock, METH_VARARGS, fcntl_flock__doc__},
+
+static PyObject *
+fcntl_flock_impl(PyModuleDef *module, int fd, int code);
+
+static PyObject *
+fcntl_flock(PyModuleDef *module, PyObject *args)
+{
+    PyObject *return_value = NULL;
+    int fd;
+    int code;
+
+    if (!PyArg_ParseTuple(args,
+        "O&i:flock",
+        conv_descriptor, &fd, &code))
+        goto exit;
+    return_value = fcntl_flock_impl(module, fd, code);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(fcntl_lockf__doc__,
+"lockf($module, fd, code, lenobj=None, startobj=None, whence=0, /)\n"
+"--\n"
+"\n"
+"A wrapper around the fcntl() locking calls.\n"
+"\n"
+"fd is the file descriptor of the file to lock or unlock, and operation is one\n"
+"of the following values:\n"
+"\n"
+"    LOCK_UN - unlock\n"
+"    LOCK_SH - acquire a shared lock\n"
+"    LOCK_EX - acquire an exclusive lock\n"
+"\n"
+"When operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with\n"
+"LOCK_NB to avoid blocking on lock acquisition.  If LOCK_NB is used and the\n"
+"lock cannot be acquired, an IOError will be raised and the exception will\n"
+"have an errno attribute set to EACCES or EAGAIN (depending on the operating\n"
+"system -- for portability, check for either value).\n"
+"\n"
+"length is the number of bytes to lock, with the default meaning to lock to\n"
+"EOF.  start is the byte offset, relative to whence, to that the lock\n"
+"starts.  whence is as with fileobj.seek(), specifically:\n"
+"\n"
+"    0 - relative to the start of the file (SEEK_SET)\n"
+"    1 - relative to the current buffer position (SEEK_CUR)\n"
+"    2 - relative to the end of the file (SEEK_END)");
+
+#define FCNTL_LOCKF_METHODDEF    \
+    {"lockf", (PyCFunction)fcntl_lockf, METH_VARARGS, fcntl_lockf__doc__},
+
+static PyObject *
+fcntl_lockf_impl(PyModuleDef *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence);
+
+static PyObject *
+fcntl_lockf(PyModuleDef *module, PyObject *args)
+{
+    PyObject *return_value = NULL;
+    int fd;
+    int code;
+    PyObject *lenobj = NULL;
+    PyObject *startobj = NULL;
+    int whence = 0;
+
+    if (!PyArg_ParseTuple(args,
+        "O&i|OOi:lockf",
+        conv_descriptor, &fd, &code, &lenobj, &startobj, &whence))
+        goto exit;
+    return_value = fcntl_lockf_impl(module, fd, code, lenobj, startobj, whence);
+
+exit:
+    return return_value;
+}
+/*[clinic end generated code: output=84bdde73a92f7c61 input=a9049054013a1b77]*/
index 56e40212d1c16bf5f85928339b91fefa89afd339..87662dd66ed74b281da2f5ad8e5144bf72f1901f 100644 (file)
 #include <stropts.h>
 #endif
 
+/*[clinic input]
+output preset file
+module fcntl
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c7356fdb126a904a]*/
+
 static int
 conv_descriptor(PyObject *object, int *target)
 {
@@ -26,48 +32,72 @@ conv_descriptor(PyObject *object, int *target)
     return 1;
 }
 
+/* Must come after conv_descriptor definition. */
+#include "clinic/fcntlmodule.c.h"
+
+/*[clinic input]
+fcntl.fcntl
+
+    fd: object(type='int', converter='conv_descriptor')
+    code: int
+    arg: object = NULL
+    /
 
-/* fcntl(fd, op, [arg]) */
+Perform the operation `code` on file descriptor fd.
+
+The values used for `code` are operating system dependent, and are available
+as constants in the fcntl module, using the same names as used in
+the relevant C header files.  The argument arg is optional, and
+defaults to 0; it may be an int or a string.  If arg is given as a string,
+the return value of fcntl is a string of that length, containing the
+resulting value put in the arg buffer by the operating system.  The length
+of the arg string is not allowed to exceed 1024 bytes.  If the arg given
+is an integer or if none is specified, the result value is an integer
+corresponding to the return value of the fcntl call in the C code.
+[clinic start generated code]*/
 
 static PyObject *
-fcntl_fcntl(PyObject *self, PyObject *args)
+fcntl_fcntl_impl(PyModuleDef *module, int fd, int code, PyObject *arg)
+/*[clinic end generated code: output=afc5bfa74a03ef0d input=4850c13a41e86930]*/
 {
-    int fd;
-    int code;
-    long arg;
+    int int_arg = 0;
     int ret;
     char *str;
     Py_ssize_t len;
     char buf[1024];
 
-    if (PyArg_ParseTuple(args, "O&is#:fcntl",
-                         conv_descriptor, &fd, &code, &str, &len)) {
-        if ((size_t)len > sizeof buf) {
-            PyErr_SetString(PyExc_ValueError,
-                            "fcntl string arg too long");
-            return NULL;
+    if (arg != NULL) {
+        int parse_result;
+
+        if (PyArg_Parse(arg, "s#", &str, &len)) {
+            if ((size_t)len > sizeof buf) {
+                PyErr_SetString(PyExc_ValueError,
+                                "fcntl string arg too long");
+                return NULL;
+            }
+            memcpy(buf, str, len);
+            Py_BEGIN_ALLOW_THREADS
+            ret = fcntl(fd, code, buf);
+            Py_END_ALLOW_THREADS
+            if (ret < 0) {
+                PyErr_SetFromErrno(PyExc_IOError);
+                return NULL;
+            }
+            return PyBytes_FromStringAndSize(buf, len);
         }
-        memcpy(buf, str, len);
-        Py_BEGIN_ALLOW_THREADS
-        ret = fcntl(fd, code, buf);
-        Py_END_ALLOW_THREADS
-        if (ret < 0) {
-            PyErr_SetFromErrno(PyExc_IOError);
-            return NULL;
+
+        PyErr_Clear();
+        parse_result = PyArg_Parse(arg,
+            "l;fcntl requires a file or file descriptor,"
+            " an integer and optionally a third integer or a string",
+            &int_arg);
+        if (!parse_result) {
+          return NULL;
         }
-        return PyBytes_FromStringAndSize(buf, len);
     }
 
-    PyErr_Clear();
-    arg = 0;
-    if (!PyArg_ParseTuple(args,
-         "O&i|l;fcntl requires a file or file descriptor,"
-         " an integer and optionally a third integer or a string",
-                          conv_descriptor, &fd, &code, &arg)) {
-      return NULL;
-    }
     Py_BEGIN_ALLOW_THREADS
-    ret = fcntl(fd, code, arg);
+    ret = fcntl(fd, code, int_arg);
     Py_END_ALLOW_THREADS
     if (ret < 0) {
         PyErr_SetFromErrno(PyExc_IOError);
@@ -76,29 +106,53 @@ fcntl_fcntl(PyObject *self, PyObject *args)
     return PyLong_FromLong((long)ret);
 }
 
-PyDoc_STRVAR(fcntl_doc,
-"fcntl(fd, op, [arg])\n\
-\n\
-Perform the operation op on file descriptor fd.  The values used\n\
-for op are operating system dependent, and are available\n\
-as constants in the fcntl module, using the same names as used in\n\
-the relevant C header files.  The argument arg is optional, and\n\
-defaults to 0; it may be an int or a string.  If arg is given as a string,\n\
-the return value of fcntl is a string of that length, containing the\n\
-resulting value put in the arg buffer by the operating system.  The length\n\
-of the arg string is not allowed to exceed 1024 bytes.  If the arg given\n\
-is an integer or if none is specified, the result value is an integer\n\
-corresponding to the return value of the fcntl call in the C code.");
 
+/*[clinic input]
+fcntl.ioctl
+
+    fd: object(type='int', converter='conv_descriptor')
+    op as code: unsigned_int(bitwise=True)
+    arg as ob_arg: object = NULL
+    mutate_flag as mutate_arg: bool = True
+    /
 
-/* ioctl(fd, op, [arg]) */
+Perform the operation op on file descriptor fd.
+
+The values used for op are operating system dependent, and are available as
+constants in the fcntl or termios library modules, using the same names as
+used in the relevant C header files.
+
+The argument `arg` is optional, and defaults to 0; it may be an int or a
+buffer containing character data (most likely a string or an array).
+
+If the argument is a mutable buffer (such as an array) and if the
+mutate_flag argument (which is only allowed in this case) is true then the
+buffer is (in effect) passed to the operating system and changes made by
+the OS will be reflected in the contents of the buffer after the call has
+returned.  The return value is the integer returned by the ioctl system
+call.
+
+If the argument is a mutable buffer and the mutable_flag argument is not
+passed or is false, the behavior is as if a string had been passed.  This
+behavior will change in future releases of Python.
+
+If the argument is an immutable buffer (most likely a string) then a copy
+of the buffer is passed to the operating system and the return value is a
+string of the same length containing whatever the operating system put in
+the buffer.  The length of the arg buffer in this case is not allowed to
+exceed 1024 bytes.
+
+If the arg given is an integer or if none is specified, the result value is
+an integer corresponding to the return value of the ioctl call in the C
+code.
+[clinic start generated code]*/
 
 static PyObject *
-fcntl_ioctl(PyObject *self, PyObject *args)
+fcntl_ioctl_impl(PyModuleDef *module, int fd, unsigned int code, PyObject *ob_arg, int mutate_arg)
+/*[clinic end generated code: output=ad47738c118622bf input=a55a6ee8e494c449]*/
 {
 #define IOCTL_BUFSZ 1024
-    int fd;
-    /* In PyArg_ParseTuple below, we use the unsigned non-checked 'I'
+    /* We use the unsigned non-checked 'I'
        format for the 'code' parameter because Python turns 0x8000000
        into either a large positive number (PyLong or PyInt on 64-bit
        platforms) or a negative number on others (32-bit PyInt)
@@ -111,101 +165,98 @@ fcntl_ioctl(PyObject *self, PyObject *args)
        in their unsigned long ioctl codes this will break and need
        special casing based on the platform being built on.
      */
-    unsigned int code;
-    int arg;
+    int arg = 0;
     int ret;
     Py_buffer pstr;
     char *str;
     Py_ssize_t len;
-    int mutate_arg = 1;
     char buf[IOCTL_BUFSZ+1];  /* argument plus NUL byte */
 
-    if (PyArg_ParseTuple(args, "O&Iw*|i:ioctl",
-                         conv_descriptor, &fd, &code,
-                         &pstr, &mutate_arg)) {
-        char *arg;
-        str = pstr.buf;
-        len = pstr.len;
-
-        if (mutate_arg) {
-            if (len <= IOCTL_BUFSZ) {
-                memcpy(buf, str, len);
-                buf[len] = '\0';
-                arg = buf;
+    if (ob_arg != NULL) {
+        if (PyArg_Parse(ob_arg, "w*:ioctl", &pstr)) {
+            char *arg;
+            str = pstr.buf;
+            len = pstr.len;
+
+            if (mutate_arg) {
+                if (len <= IOCTL_BUFSZ) {
+                    memcpy(buf, str, len);
+                    buf[len] = '\0';
+                    arg = buf;
+                }
+                else {
+                    arg = str;
+                }
+            }
+            else {
+                if (len > IOCTL_BUFSZ) {
+                    PyBuffer_Release(&pstr);
+                    PyErr_SetString(PyExc_ValueError,
+                        "ioctl string arg too long");
+                    return NULL;
+                }
+                else {
+                    memcpy(buf, str, len);
+                    buf[len] = '\0';
+                    arg = buf;
+                }
+            }
+            if (buf == arg) {
+                Py_BEGIN_ALLOW_THREADS /* think array.resize() */
+                ret = ioctl(fd, code, arg);
+                Py_END_ALLOW_THREADS
+            }
+            else {
+                ret = ioctl(fd, code, arg);
+            }
+            if (mutate_arg && (len <= IOCTL_BUFSZ)) {
+                memcpy(str, buf, len);
+            }
+            PyBuffer_Release(&pstr); /* No further access to str below this point */
+            if (ret < 0) {
+                PyErr_SetFromErrno(PyExc_IOError);
+                return NULL;
+            }
+            if (mutate_arg) {
+                return PyLong_FromLong(ret);
             }
             else {
-                arg = str;
+                return PyBytes_FromStringAndSize(buf, len);
             }
         }
-        else {
+
+        PyErr_Clear();
+        if (PyArg_Parse(ob_arg, "s*:ioctl", &pstr)) {
+            str = pstr.buf;
+            len = pstr.len;
             if (len > IOCTL_BUFSZ) {
                 PyBuffer_Release(&pstr);
                 PyErr_SetString(PyExc_ValueError,
-                    "ioctl string arg too long");
+                                "ioctl string arg too long");
                 return NULL;
             }
-            else {
-                memcpy(buf, str, len);
-                buf[len] = '\0';
-                arg = buf;
-            }
-        }
-        if (buf == arg) {
-            Py_BEGIN_ALLOW_THREADS /* think array.resize() */
-            ret = ioctl(fd, code, arg);
+            memcpy(buf, str, len);
+            buf[len] = '\0';
+            Py_BEGIN_ALLOW_THREADS
+            ret = ioctl(fd, code, buf);
             Py_END_ALLOW_THREADS
-        }
-        else {
-            ret = ioctl(fd, code, arg);
-        }
-        if (mutate_arg && (len <= IOCTL_BUFSZ)) {
-            memcpy(str, buf, len);
-        }
-        PyBuffer_Release(&pstr); /* No further access to str below this point */
-        if (ret < 0) {
-            PyErr_SetFromErrno(PyExc_IOError);
-            return NULL;
-        }
-        if (mutate_arg) {
-            return PyLong_FromLong(ret);
-        }
-        else {
+            if (ret < 0) {
+                PyBuffer_Release(&pstr);
+                PyErr_SetFromErrno(PyExc_IOError);
+                return NULL;
+            }
+            PyBuffer_Release(&pstr);
             return PyBytes_FromStringAndSize(buf, len);
         }
-    }
 
-    PyErr_Clear();
-    if (PyArg_ParseTuple(args, "O&Is*:ioctl",
-                         conv_descriptor, &fd, &code, &pstr)) {
-        str = pstr.buf;
-        len = pstr.len;
-        if (len > IOCTL_BUFSZ) {
-            PyBuffer_Release(&pstr);
-            PyErr_SetString(PyExc_ValueError,
-                            "ioctl string arg too long");
-            return NULL;
+        PyErr_Clear();
+        if (!PyArg_Parse(ob_arg,
+             "i;ioctl requires a file or file descriptor,"
+             " an integer and optionally an integer or buffer argument",
+             &arg)) {
+          return NULL;
         }
-        memcpy(buf, str, len);
-        buf[len] = '\0';
-        Py_BEGIN_ALLOW_THREADS
-        ret = ioctl(fd, code, buf);
-        Py_END_ALLOW_THREADS
-        if (ret < 0) {
-            PyBuffer_Release(&pstr);
-            PyErr_SetFromErrno(PyExc_IOError);
-            return NULL;
-        }
-        PyBuffer_Release(&pstr);
-        return PyBytes_FromStringAndSize(buf, len);
-    }
-
-    PyErr_Clear();
-    arg = 0;
-    if (!PyArg_ParseTuple(args,
-         "O&I|i;ioctl requires a file or file descriptor,"
-         " an integer and optionally an integer or buffer argument",
-                          conv_descriptor, &fd, &code, &arg)) {
-      return NULL;
+        // Fall-through to outside the 'if' statement.
     }
     Py_BEGIN_ALLOW_THREADS
     ret = ioctl(fd, code, arg);
@@ -218,52 +269,25 @@ fcntl_ioctl(PyObject *self, PyObject *args)
 #undef IOCTL_BUFSZ
 }
 
-PyDoc_STRVAR(ioctl_doc,
-"ioctl(fd, op[, arg[, mutate_flag]])\n\
-\n\
-Perform the operation op on file descriptor fd.  The values used for op\n\
-are operating system dependent, and are available as constants in the\n\
-fcntl or termios library modules, using the same names as used in the\n\
-relevant C header files.\n\
-\n\
-The argument arg is optional, and defaults to 0; it may be an int or a\n\
-buffer containing character data (most likely a string or an array). \n\
-\n\
-If the argument is a mutable buffer (such as an array) and if the\n\
-mutate_flag argument (which is only allowed in this case) is true then the\n\
-buffer is (in effect) passed to the operating system and changes made by\n\
-the OS will be reflected in the contents of the buffer after the call has\n\
-returned.  The return value is the integer returned by the ioctl system\n\
-call.\n\
-\n\
-If the argument is a mutable buffer and the mutable_flag argument is not\n\
-passed or is false, the behavior is as if a string had been passed.  This\n\
-behavior will change in future releases of Python.\n\
-\n\
-If the argument is an immutable buffer (most likely a string) then a copy\n\
-of the buffer is passed to the operating system and the return value is a\n\
-string of the same length containing whatever the operating system put in\n\
-the buffer.  The length of the arg buffer in this case is not allowed to\n\
-exceed 1024 bytes.\n\
-\n\
-If the arg given is an integer or if none is specified, the result value is\n\
-an integer corresponding to the return value of the ioctl call in the C\n\
-code.");
-
-
-/* flock(fd, operation) */
+/*[clinic input]
+fcntl.flock
+
+    fd: object(type='int', converter='conv_descriptor')
+    code: int
+    /
+
+Perform the lock operation op on file descriptor fd.
+
+See the Unix manual page for flock(2) for details (On some systems, this
+function is emulated using fcntl()).
+[clinic start generated code]*/
 
 static PyObject *
-fcntl_flock(PyObject *self, PyObject *args)
+fcntl_flock_impl(PyModuleDef *module, int fd, int code)
+/*[clinic end generated code: output=c9035133a7dbfc96 input=b762aa9448d05e43]*/
 {
-    int fd;
-    int code;
     int ret;
 
-    if (!PyArg_ParseTuple(args, "O&i:flock",
-                          conv_descriptor, &fd, &code))
-        return NULL;
-
 #ifdef HAVE_FLOCK
     Py_BEGIN_ALLOW_THREADS
     ret = flock(fd, code);
@@ -299,29 +323,49 @@ fcntl_flock(PyObject *self, PyObject *args)
         PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
-    Py_INCREF(Py_None);
-    return Py_None;
+    Py_RETURN_NONE;
 }
 
-PyDoc_STRVAR(flock_doc,
-"flock(fd, operation)\n\
-\n\
-Perform the lock operation op on file descriptor fd.  See the Unix \n\
-manual page for flock(2) for details.  (On some systems, this function is\n\
-emulated using fcntl().)");
 
+/*[clinic input]
+fcntl.lockf
+
+    fd: object(type='int', converter='conv_descriptor')
+    code: int
+    lenobj: object = NULL
+    startobj: object = NULL
+    whence: int = 0
+    /
+
+A wrapper around the fcntl() locking calls.
+
+fd is the file descriptor of the file to lock or unlock, and operation is one
+of the following values:
+
+    LOCK_UN - unlock
+    LOCK_SH - acquire a shared lock
+    LOCK_EX - acquire an exclusive lock
+
+When operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with
+LOCK_NB to avoid blocking on lock acquisition.  If LOCK_NB is used and the
+lock cannot be acquired, an IOError will be raised and the exception will
+have an errno attribute set to EACCES or EAGAIN (depending on the operating
+system -- for portability, check for either value).
+
+length is the number of bytes to lock, with the default meaning to lock to
+EOF.  start is the byte offset, relative to whence, to that the lock
+starts.  whence is as with fileobj.seek(), specifically:
+
+    0 - relative to the start of the file (SEEK_SET)
+    1 - relative to the current buffer position (SEEK_CUR)
+    2 - relative to the end of the file (SEEK_END)
+[clinic start generated code]*/
 
-/* lockf(fd, operation) */
 static PyObject *
-fcntl_lockf(PyObject *self, PyObject *args)
+fcntl_lockf_impl(PyModuleDef *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence)
+/*[clinic end generated code: output=5536df2892bf3ce9 input=44856fa06db36184]*/
 {
-    int fd, code, ret, whence = 0;
-    PyObject *lenobj = NULL, *startobj = NULL;
-
-    if (!PyArg_ParseTuple(args, "O&i|OOi:lockf",
-                          conv_descriptor, &fd, &code,
-                          &lenobj, &startobj, &whence))
-        return NULL;
+    int ret;
 
 #ifndef LOCK_SH
 #define LOCK_SH         1       /* shared lock */
@@ -374,43 +418,17 @@ fcntl_lockf(PyObject *self, PyObject *args)
         PyErr_SetFromErrno(PyExc_IOError);
         return NULL;
     }
-    Py_INCREF(Py_None);
-    return Py_None;
+    Py_RETURN_NONE;
 }
 
-PyDoc_STRVAR(lockf_doc,
-"lockf (fd, operation, length=0, start=0, whence=0)\n\
-\n\
-This is essentially a wrapper around the fcntl() locking calls.  fd is the\n\
-file descriptor of the file to lock or unlock, and operation is one of the\n\
-following values:\n\
-\n\
-    LOCK_UN - unlock\n\
-    LOCK_SH - acquire a shared lock\n\
-    LOCK_EX - acquire an exclusive lock\n\
-\n\
-When operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with\n\
-LOCK_NB to avoid blocking on lock acquisition.  If LOCK_NB is used and the\n\
-lock cannot be acquired, an IOError will be raised and the exception will\n\
-have an errno attribute set to EACCES or EAGAIN (depending on the operating\n\
-system -- for portability, check for either value).\n\
-\n\
-length is the number of bytes to lock, with the default meaning to lock to\n\
-EOF.  start is the byte offset, relative to whence, to that the lock\n\
-starts.  whence is as with fileobj.seek(), specifically:\n\
-\n\
-    0 - relative to the start of the file (SEEK_SET)\n\
-    1 - relative to the current buffer position (SEEK_CUR)\n\
-    2 - relative to the end of the file (SEEK_END)");
-
 /* List of functions */
 
 static PyMethodDef fcntl_methods[] = {
-    {"fcntl",           fcntl_fcntl, METH_VARARGS, fcntl_doc},
-    {"ioctl",           fcntl_ioctl, METH_VARARGS, ioctl_doc},
-    {"flock",           fcntl_flock, METH_VARARGS, flock_doc},
-    {"lockf",       fcntl_lockf, METH_VARARGS, lockf_doc},
-    {NULL,              NULL}           /* sentinel */
+    FCNTL_FCNTL_METHODDEF
+    FCNTL_IOCTL_METHODDEF
+    FCNTL_FLOCK_METHODDEF
+    FCNTL_LOCKF_METHODDEF
+    {NULL, NULL}  /* sentinel */
 };