From 8b08ed5249ae08442dacb18f0b50530b4c8bb6d7 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 9 Oct 2024 18:07:24 +0300 Subject: [PATCH 1/2] impl --- Lib/functools.py | 4 ++++ Lib/test/test_functools.py | 6 ++++++ Modules/_functoolsmodule.c | 33 ++++++++++++++++++--------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index 9d53d3601559b2..c42f608c38be6c 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -326,6 +326,10 @@ def _partial_new(cls, func, /, *args, **keywords): "or a descriptor") if args and args[-1] is Placeholder: raise TypeError("trailing Placeholders are not allowed") + if keywords: + for v in keywords.values(): + if v is Placeholder: + raise TypeError("keyword Placeholders are not allowed") if isinstance(func, base_cls): pto_phcount = func._phcount tot_args = func.args diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index bdaa9a7ec4f020..5d937424218c2a 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -249,6 +249,12 @@ def test_placeholders_optimization(self): self.assertEqual(p2.args, (PH, 0)) self.assertEqual(p2(1), ((1, 0), {})) + def test_placeholders_kw_restriction(self): + PH = self.module.Placeholder + exc_string = 'keyword Placeholders are not allowed' + with self.assertRaisesRegex(TypeError, exc_string): + self.partial(capture, a=PH) + def test_construct_placeholder_singleton(self): PH = self.module.Placeholder tp = type(PH) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 31cf7bcc09782c..ceaa3fa5e24792 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -277,30 +277,33 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) assert(PyTuple_Check(pto->args)); } + /* process keywords */ if (pto_kw == NULL || PyDict_GET_SIZE(pto_kw) == 0) { - if (kw == NULL) { - pto->kw = PyDict_New(); - } - else if (Py_REFCNT(kw) == 1) { - pto->kw = Py_NewRef(kw); - } - else { - pto->kw = PyDict_Copy(kw); - } + pto->kw = PyDict_New(); } else { pto->kw = PyDict_Copy(pto_kw); - if (kw != NULL && pto->kw != NULL) { - if (PyDict_Merge(pto->kw, kw, 1) != 0) { - Py_DECREF(pto); - return NULL; - } - } } if (pto->kw == NULL) { Py_DECREF(pto); return NULL; } + if (kw != NULL) { + PyObject *key, *val; + Py_ssize_t pos = 0; + while (PyDict_Next(kw, &pos, &key, &val)) { + if (val == phold) { + Py_DECREF(pto); + PyErr_SetString(PyExc_TypeError, + "keyword Placeholders are not allowed"); + return NULL; + } + if (PyDict_SetItem(pto->kw, key, val)) { + Py_DECREF(pto); + return NULL; + } + } + } partial_setvectorcall(pto); return (PyObject *)pto; From bea722a6b7a76d73d9fcff5ea424c710570894f6 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:16:11 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-10-09-15-16-09.gh-issue-125028.IOzxPL.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-10-09-15-16-09.gh-issue-125028.IOzxPL.rst diff --git a/Misc/NEWS.d/next/Library/2024-10-09-15-16-09.gh-issue-125028.IOzxPL.rst b/Misc/NEWS.d/next/Library/2024-10-09-15-16-09.gh-issue-125028.IOzxPL.rst new file mode 100644 index 00000000000000..8320d6b6c10a1d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-09-15-16-09.gh-issue-125028.IOzxPL.rst @@ -0,0 +1 @@ +:data:`functools.Placeholder` is not allowed in :func:`functools.partial` keyword vaules.