From 0ec432c88289d565cda0f41ab4c3ebd2e548ac7b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 9 Nov 2019 23:05:41 +0900 Subject: [PATCH 01/10] bpo-22367: Add open_file_descriptor parameter to fcntl.lockf() --- Doc/whatsnew/3.9.rst | 4 ++ Lib/test/test_fcntl.py | 8 ++++ .../2019-11-09-23-05-07.bpo-22367.Ygahn1.rst | 2 + Modules/clinic/fcntlmodule.c.h | 40 ++++++++++++++----- Modules/fcntlmodule.c | 15 ++++++- 5 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 281173edb895b3e..c4b46e9301a009c 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -147,6 +147,10 @@ Added constants :data:`~fcntl.F_OFD_GETLK`, :data:`~fcntl.F_OFD_SETLK` and :data:`~fcntl.F_OFD_SETLKW`. (Contributed by Dong-hee Na in :issue:`38602`.) +Add an optional keyword argument ``open_file_descriptor`` +to :func:`fcntl.lockf` function. +(Contributed by Dong-hee Na in :issue:`22367`.) + os -- diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 9ab68c67241f464..629546066caec76 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -164,6 +164,14 @@ def test_lockf_exclusive(self): fcntl.lockf(self.f, fcntl.LOCK_UN) self.assertEqual(p.exitcode, 0) + @unittest.skipUnless(hasattr(fcntl, 'F_OFD_GETLK'), 'requires open file description locks') + def test_lockf_exclusive_ofd(self): + self.f = open(TESTFN, 'wb+') + fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB, open_file_descriptor=True) + fcntl.lockf(self.f, fcntl.LOCK_UN, open_file_descriptor=True) + fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB, open_file_descriptor=True) + fcntl.lockf(self.f, fcntl.LOCK_UN, open_file_descriptor=True) + @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") def test_lockf_share(self): self.f = open(TESTFN, 'wb+') diff --git a/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst b/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst new file mode 100644 index 000000000000000..9520e6f5225ca05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst @@ -0,0 +1,2 @@ +Add an optional keyword argument ``open_file_descriptor`` to +:func:`fcntl.lockf` function. Patch by Dong-hee Na. diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 024a44cfbf8bc60..a5f94238dde5191 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -184,7 +184,8 @@ fcntl_flock(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(fcntl_lockf__doc__, -"lockf($module, fd, cmd, len=0, start=0, whence=0, /)\n" +"lockf($module, fd, cmd, len=0, start=0, whence=0, /,\n" +" open_file_descriptor=False)\n" "--\n" "\n" "A wrapper around the fcntl() locking calls.\n" @@ -211,23 +212,29 @@ PyDoc_STRVAR(fcntl_lockf__doc__, " 2 - relative to the end of the file (SEEK_END)"); #define FCNTL_LOCKF_METHODDEF \ - {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL, fcntl_lockf__doc__}, + {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL|METH_KEYWORDS, fcntl_lockf__doc__}, static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, - PyObject *startobj, int whence); + PyObject *startobj, int whence, int open_file_descriptor); static PyObject * -fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "", "", "", "", "open_file_descriptor", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "lockf", 0}; + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; int fd; int code; PyObject *lenobj = NULL; PyObject *startobj = NULL; int whence = 0; + int open_file_descriptor = 0; - if (!_PyArg_CheckPositional("lockf", nargs, 2, 5)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 6, 0, argsbuf); + if (!args) { goto exit; } if (!conv_descriptor(args[0], &fd)) { @@ -243,16 +250,19 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } if (nargs < 3) { - goto skip_optional; + goto skip_optional_posonly; } + noptargs--; lenobj = args[2]; if (nargs < 4) { - goto skip_optional; + goto skip_optional_posonly; } + noptargs--; startobj = args[3]; if (nargs < 5) { - goto skip_optional; + goto skip_optional_posonly; } + noptargs--; if (PyFloat_Check(args[4])) { PyErr_SetString(PyExc_TypeError, "integer argument expected, got float" ); @@ -262,10 +272,18 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (whence == -1 && PyErr_Occurred()) { goto exit; } -skip_optional: - return_value = fcntl_lockf_impl(module, fd, code, lenobj, startobj, whence); +skip_optional_posonly: + if (!noptargs) { + goto skip_optional_pos; + } + open_file_descriptor = PyObject_IsTrue(args[5]); + if (open_file_descriptor < 0) { + goto exit; + } +skip_optional_pos: + return_value = fcntl_lockf_impl(module, fd, code, lenobj, startobj, whence, open_file_descriptor); exit: return return_value; } -/*[clinic end generated code: output=e912d25e28362c52 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5e734dc440dd16a0 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 11906aa58292961..3ce21fe8bd4706a 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -339,6 +339,7 @@ fcntl.lockf start as startobj: object(c_default='NULL') = 0 whence: int = 0 / + open_file_descriptor: bool = False A wrapper around the fcntl() locking calls. @@ -366,8 +367,8 @@ starts. `whence` is as with fileobj.seek(), specifically: static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, - PyObject *startobj, int whence) -/*[clinic end generated code: output=4985e7a172e7461a input=3a5dc01b04371f1a]*/ + PyObject *startobj, int whence, int open_file_descriptor) +/*[clinic end generated code: output=eba798e67a9aee86 input=f0c8082b6b54132b]*/ { int ret; int async_err = 0; @@ -417,6 +418,16 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, l.l_whence = whence; do { Py_BEGIN_ALLOW_THREADS + if (open_file_descriptor) { +#ifdef F_OFD_SETLK + l.l_pid = 0; + ret = fcntl(fd, (code & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &l); +#else + PyErr_SetString(PyExc_NotImplementedError, + "lockf: open_file_descriptor is not supported on this platform"); + return NULL; +#endif + } else ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); Py_END_ALLOW_THREADS } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals())); From e462d07e490cacbcf23a980e16b99f7a825f287b Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 9 Nov 2019 23:51:02 +0900 Subject: [PATCH 02/10] bpo-22367: Update docs --- Doc/library/fcntl.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 5c172b836acca9f..276a63edb414ca4 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -123,7 +123,7 @@ The module defines the following functions: If the :c:func:`flock` fails, an :exc:`OSError` exception is raised. -.. function:: lockf(fd, cmd, len=0, start=0, whence=0) +.. function:: lockf(fd, cmd, len=0, start=0, whence=0, /, open_file_descriptor=False) This is essentially a wrapper around the :func:`~fcntl.fcntl` locking calls. *fd* is the file descriptor (file objects providing a :meth:`~io.IOBase.fileno` @@ -155,6 +155,12 @@ The module defines the following functions: The default for *len* is 0 which means to lock to the end of the file. The default for *whence* is also 0. + The default for *open_file_descriptor* is False, if *open_file_descriptor* is + set to True, open file description locks are used. + + .. versionchanged:: 3.9 + The `open_file_descriptor` keyword argument was added. + Examples (all on a SVR4 compliant system):: import struct, fcntl, os From 2414598745715d9675e6b15f5b6502fb75327b80 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sat, 9 Nov 2019 23:58:39 +0900 Subject: [PATCH 03/10] bpo-22367: Update docstring --- Modules/clinic/fcntlmodule.c.h | 10 ++++++++-- Modules/fcntlmodule.c | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index a5f94238dde5191..18297e8be2a0ba5 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -209,7 +209,13 @@ PyDoc_STRVAR(fcntl_lockf__doc__, "\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)"); +" 2 - relative to the end of the file (SEEK_END)\n" +"\n" +"The default for `start` is 0, which means to start at the beginning of the file.\n" +"The default for `len` is 0 which means to lock to the end of the file.\n" +"The default for `whence` is also 0.\n" +"The default for `open_file_descriptor` is False,\n" +"if `open_file_descriptor` is set to True, open file description locks are used."); #define FCNTL_LOCKF_METHODDEF \ {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL|METH_KEYWORDS, fcntl_lockf__doc__}, @@ -286,4 +292,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject exit: return return_value; } -/*[clinic end generated code: output=5e734dc440dd16a0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8afa3b7be9a70b58 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 3ce21fe8bd4706a..ad521596a703797 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -363,12 +363,18 @@ 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) + +The default for `start` is 0, which means to start at the beginning of the file. +The default for `len` is 0 which means to lock to the end of the file. +The default for `whence` is also 0. +The default for `open_file_descriptor` is False, +if `open_file_descriptor` is set to True, open file description locks are used. [clinic start generated code]*/ static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence, int open_file_descriptor) -/*[clinic end generated code: output=eba798e67a9aee86 input=f0c8082b6b54132b]*/ +/*[clinic end generated code: output=eba798e67a9aee86 input=fa082acb66abf9e1]*/ { int ret; int async_err = 0; From aafcda4232f205b25377517a304583fc564cd5df Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 10 Nov 2019 00:04:53 +0900 Subject: [PATCH 04/10] bpo-22367: Update what's news --- Doc/whatsnew/3.9.rst | 2 +- .../next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index c4b46e9301a009c..a3eb0f64de986a7 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -147,7 +147,7 @@ Added constants :data:`~fcntl.F_OFD_GETLK`, :data:`~fcntl.F_OFD_SETLK` and :data:`~fcntl.F_OFD_SETLKW`. (Contributed by Dong-hee Na in :issue:`38602`.) -Add an optional keyword argument ``open_file_descriptor`` +Add an optional keyword argument *open_file_descriptor* to :func:`fcntl.lockf` function. (Contributed by Dong-hee Na in :issue:`22367`.) diff --git a/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst b/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst index 9520e6f5225ca05..8b49b59ec58d7bf 100644 --- a/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst +++ b/Misc/NEWS.d/next/Library/2019-11-09-23-05-07.bpo-22367.Ygahn1.rst @@ -1,2 +1,2 @@ -Add an optional keyword argument ``open_file_descriptor`` to +Add an optional keyword argument *open_file_descriptor* to :func:`fcntl.lockf` function. Patch by Dong-hee Na. From 7ef3660501cb4a0714dd5b478649c4c49f9eab54 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 10 Nov 2019 00:08:00 +0900 Subject: [PATCH 05/10] bpo-22367: Update test name --- Lib/test/test_fcntl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 629546066caec76..7a47f3adda9178b 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -165,7 +165,7 @@ def test_lockf_exclusive(self): self.assertEqual(p.exitcode, 0) @unittest.skipUnless(hasattr(fcntl, 'F_OFD_GETLK'), 'requires open file description locks') - def test_lockf_exclusive_ofd(self): + def test_lockf_open_file_descriptor(self): self.f = open(TESTFN, 'wb+') fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB, open_file_descriptor=True) fcntl.lockf(self.f, fcntl.LOCK_UN, open_file_descriptor=True) From 66e529a595e8c2ee5fd1cd0a411f279938e0d8e8 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 10 Nov 2019 00:17:20 +0900 Subject: [PATCH 06/10] bpo-22367: update doc --- Doc/library/fcntl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 276a63edb414ca4..5713c5128a84769 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -159,7 +159,7 @@ The module defines the following functions: set to True, open file description locks are used. .. versionchanged:: 3.9 - The `open_file_descriptor` keyword argument was added. + The *open_file_descriptor* keyword argument was added. Examples (all on a SVR4 compliant system):: From 2df7fb7ab44b567ad78022edd7a31ac72fc11050 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 10 Nov 2019 00:36:50 +0900 Subject: [PATCH 07/10] bpo-22367: Apply code review --- Modules/clinic/fcntlmodule.c.h | 6 +----- Modules/fcntlmodule.c | 11 ++++------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 18297e8be2a0ba5..4c923ecc14965c1 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -211,10 +211,6 @@ PyDoc_STRVAR(fcntl_lockf__doc__, " 1 - relative to the current buffer position (SEEK_CUR)\n" " 2 - relative to the end of the file (SEEK_END)\n" "\n" -"The default for `start` is 0, which means to start at the beginning of the file.\n" -"The default for `len` is 0 which means to lock to the end of the file.\n" -"The default for `whence` is also 0.\n" -"The default for `open_file_descriptor` is False,\n" "if `open_file_descriptor` is set to True, open file description locks are used."); #define FCNTL_LOCKF_METHODDEF \ @@ -292,4 +288,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject exit: return return_value; } -/*[clinic end generated code: output=8afa3b7be9a70b58 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=80a3bf605624c78f input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index ad521596a703797..28eb7d928277250 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -364,17 +364,13 @@ starts. `whence` is as with fileobj.seek(), specifically: 1 - relative to the current buffer position (SEEK_CUR) 2 - relative to the end of the file (SEEK_END) -The default for `start` is 0, which means to start at the beginning of the file. -The default for `len` is 0 which means to lock to the end of the file. -The default for `whence` is also 0. -The default for `open_file_descriptor` is False, if `open_file_descriptor` is set to True, open file description locks are used. [clinic start generated code]*/ static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence, int open_file_descriptor) -/*[clinic end generated code: output=eba798e67a9aee86 input=fa082acb66abf9e1]*/ +/*[clinic end generated code: output=eba798e67a9aee86 input=83190651f560f35e]*/ { int ret; int async_err = 0; @@ -433,8 +429,9 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, "lockf: open_file_descriptor is not supported on this platform"); return NULL; #endif - } else - ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + } else { + ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + } Py_END_ALLOW_THREADS } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } From 0a505c78107225509d60b90eb3b73d0af15b6a09 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 10 Nov 2019 22:17:58 +0900 Subject: [PATCH 08/10] bpo-22367: Add unit test from gnu example --- Lib/test/test_fcntl.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 7a47f3adda9178b..48895c807ebacef 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -4,6 +4,8 @@ import os import struct import sys +import threading +import time import unittest from multiprocessing import Process from test.support import (verbose, TESTFN, unlink, run_unittest, import_module, @@ -172,6 +174,35 @@ def test_lockf_open_file_descriptor(self): fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB, open_file_descriptor=True) fcntl.lockf(self.f, fcntl.LOCK_UN, open_file_descriptor=True) + @unittest.skipUnless(hasattr(fcntl, 'F_OFD_GETLK'), 'requires open file description locks') + def test_open_file_descriptor_example(self): + threads = [] + num_threads = 3 + num_iterations = 5 + self.f = open(TESTFN, 'w+') + def thread_start(thread_id): + for i in range(num_iterations): + fcntl.lockf(self.f, fcntl.LOCK_EX, open_file_descriptor=True) + self.f.seek(0, os.SEEK_END) + self.f.write(f'{i}: tid={thread_id}, fd={self.f.fileno()}\n') + fcntl.lockf(self.f, fcntl.LOCK_UN, open_file_descriptor=True) + time.sleep(0.05) + + for i in range(num_threads): + t = threading.Thread(target=thread_start, args=(i,)) + threads.append(t) + + for t in threads: + t.start() + + for t in threads: + t.join() + + self.f.close() + self.f = open(TESTFN, 'r') + res = self.f.readlines() + self.assertEqual(len(res), num_threads*num_iterations) + @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") def test_lockf_share(self): self.f = open(TESTFN, 'wb+') From 6c994fc8196066602110914d2063bb109c892bef Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Wed, 13 Nov 2019 18:08:29 +0900 Subject: [PATCH 09/10] bpo-22367: Update docs --- Doc/library/fcntl.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 5713c5128a84769..06f01bd212126d5 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -156,10 +156,12 @@ The module defines the following functions: default for *whence* is also 0. The default for *open_file_descriptor* is False, if *open_file_descriptor* is - set to True, open file description locks are used. + set to True, open file description locks are used. Note that open file description + locks features are suppoted on Linux(>=3.15) otherwise it will raise :exc:`NotImplementedError` .. versionchanged:: 3.9 The *open_file_descriptor* keyword argument was added. + ref: https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html Examples (all on a SVR4 compliant system):: From 79534ae9124866ead515703b1d9c8dcf702e9c45 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Wed, 13 Nov 2019 18:11:29 +0900 Subject: [PATCH 10/10] bpo-22367: Fix typo --- Doc/library/fcntl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 06f01bd212126d5..eb94a7ef4c5c7e3 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -157,7 +157,7 @@ The module defines the following functions: The default for *open_file_descriptor* is False, if *open_file_descriptor* is set to True, open file description locks are used. Note that open file description - locks features are suppoted on Linux(>=3.15) otherwise it will raise :exc:`NotImplementedError` + locks features are supported on Linux(>=3.15) otherwise it will raise :exc:`NotImplementedError` .. versionchanged:: 3.9 The *open_file_descriptor* keyword argument was added.