From 83b5e620da941d943e8ec12ef7d47cfd550da9f3 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Sat, 27 May 2017 19:14:26 -0700 Subject: [PATCH 1/9] Adds cell_set_contents. --- Objects/cellobject.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 9f4eddb846835c3..ed92f48ba4c97f3 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -140,8 +140,21 @@ cell_get_contents(PyCellObject *op, void *closure) return op->ob_ref; } +int +cell_set_contents(PyCellObject *op, PyCellObject *obj) +{ + if (!PyCell_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + Py_INCREF(obj); + Py_SETREF(op->ob_ref, obj); + return 0; +} + static PyGetSetDef cell_getsetlist[] = { - {"cell_contents", (getter)cell_get_contents, NULL}, + {"cell_contents", (getter)cell_get_contents, + (setter)cell_set_contents, NULL}, {NULL} /* sentinel */ }; From 9b4cfd28647707a131e8e315bc2fa88ec584417b Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Sat, 27 May 2017 21:56:54 -0700 Subject: [PATCH 2/9] Not using Py_SETREF bc it uses PyObject instead of PyCellObject. --- Objects/cellobject.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Objects/cellobject.c b/Objects/cellobject.c index ed92f48ba4c97f3..2b99ae2d346b3f5 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -143,12 +143,10 @@ cell_get_contents(PyCellObject *op, void *closure) int cell_set_contents(PyCellObject *op, PyCellObject *obj) { - if (!PyCell_Check(op)) { - PyErr_BadInternalCall(); - return -1; - } Py_INCREF(obj); - Py_SETREF(op->ob_ref, obj); + PyCellObject *tmp_op = (PyCellObject *)(op->ob_ref); + (op->ob_ref) = obj; + Py_DECREF(tmp_op); return 0; } From 244cb7ea4949f696351b139e038d87cd629a1251 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 31 May 2017 21:27:18 -0700 Subject: [PATCH 3/9] Updates to use XSETREF. Adds docs, tests, and news update. --- Doc/reference/datamodel.rst | 2 +- Lib/test/test_funcattrs.py | 14 ++++++++++++++ Misc/NEWS | 2 ++ Objects/cellobject.c | 10 +++++----- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 095a2380b379bc9..96ee0d122ca678b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -507,7 +507,7 @@ Callable types | | arbitrary function | | | | attributes. | | +-------------------------+-------------------------------+-----------+ - | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | + | :attr:`__closure__` | ``None`` or a tuple of cells | Writable | | | that contain bindings for the | | | | function's free variables. | | +-------------------------+-------------------------------+-----------+ diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 8f481bb64e18e60..9d45d10b02e7bab 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -93,6 +93,20 @@ def f(): print(a) self.fail("shouldn't be able to read an empty cell") a = 12 + def test_set_cell(self): + a = 12 + def f(): print(a) + c = f.__closure__ + c[0].cell_contents = 9 + self.assertEqual(c[0].cell_contents, 9) + c[0].cell_contents = None + try: + c[0].cell_contents + except ValueError: + pass + else: + self.fail("shouldn't be able to read an empty cell") + def test___name__(self): self.assertEqual(self.b.__name__, 'b') self.b.__name__ = 'c' diff --git a/Misc/NEWS b/Misc/NEWS index b7990c62e4f7441..d1bb7dfd3f8a015 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -66,6 +66,8 @@ Core and Builtins - bpo-29546: Improve from-import error message with location +- bpo-30486: Allows setting cell values for __closure__. Patch by Lisa Roach. + - Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0]. - Issue #29337: Fixed possible BytesWarning when compare the code objects. diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 2b99ae2d346b3f5..7d57bf4c8c29e07 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -141,12 +141,12 @@ cell_get_contents(PyCellObject *op, void *closure) } int -cell_set_contents(PyCellObject *op, PyCellObject *obj) +cell_set_contents(PyCellObject *op, PyObject *obj) { - Py_INCREF(obj); - PyCellObject *tmp_op = (PyCellObject *)(op->ob_ref); - (op->ob_ref) = obj; - Py_DECREF(tmp_op); + if (obj == Py_None) + obj = NULL; + Py_XINCREF(obj); + Py_XSETREF(op->ob_ref, obj); return 0; } From c5db387b8cad23e35a94865cf9ab29a992120117 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Fri, 2 Jun 2017 08:47:47 -0700 Subject: [PATCH 4/9] cell_contents can be set to none. Update documentation. --- Doc/reference/datamodel.rst | 6 +++++- Lib/test/test_funcattrs.py | 2 +- Objects/cellobject.c | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 96ee0d122ca678b..2baa195657cf35b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -507,9 +507,13 @@ Callable types | | arbitrary function | | | | attributes. | | +-------------------------+-------------------------------+-----------+ - | :attr:`__closure__` | ``None`` or a tuple of cells | Writable | + | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | | | that contain bindings for the | | | | function's free variables. | | + | | The ``cell_contents`` | | + | | attribute can be used for | | + | | used for reading and writing | | + | | to the cells. | | +-------------------------+-------------------------------+-----------+ | :attr:`__annotations__` | A dict containing annotations | Writable | | | of parameters. The keys of | | diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 9d45d10b02e7bab..13252e93e6cf670 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -99,7 +99,7 @@ def f(): print(a) c = f.__closure__ c[0].cell_contents = 9 self.assertEqual(c[0].cell_contents, 9) - c[0].cell_contents = None + del c[0].cell_contents try: c[0].cell_contents except ValueError: diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 7d57bf4c8c29e07..6af93b0030815ec 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -143,8 +143,6 @@ cell_get_contents(PyCellObject *op, void *closure) int cell_set_contents(PyCellObject *op, PyObject *obj) { - if (obj == Py_None) - obj = NULL; Py_XINCREF(obj); Py_XSETREF(op->ob_ref, obj); return 0; From 02dbd0ed47b63253fd12d74e01fc6f328ca51414 Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Mon, 5 Jun 2017 20:37:30 -0700 Subject: [PATCH 5/9] Updates test with return value and updates documentation.: --- Doc/reference/datamodel.rst | 12 +++++++----- Lib/test/test_funcattrs.py | 9 ++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 2baa195657cf35b..6867ff42b72572d 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -510,10 +510,9 @@ Callable types | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | | | that contain bindings for the | | | | function's free variables. | | - | | The ``cell_contents`` | | - | | attribute can be used for | | - | | used for reading and writing | | - | | to the cells. | | + | | See below for information on | | + | | the ``cell_contents`` | | + | | attribute. | | +-------------------------+-------------------------------+-----------+ | :attr:`__annotations__` | A dict containing annotations | Writable | | | of parameters. The keys of | | @@ -532,7 +531,10 @@ Callable types can be used, for example, to attach metadata to functions. Regular attribute dot-notation is used to get and set such attributes. *Note that the current implementation only supports function attributes on user-defined functions. - Function attributes on built-in functions may be supported in the future.* + Function attributes on built-in functions may be supported in the future. + + A cell object has the attribute ``cell_contents``. This can be used to get + the value of the cell, as well as set the value. Additional information about a function's definition can be retrieved from its code object; see the description of internal types below. diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 13252e93e6cf670..1b18649427529fc 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -95,10 +95,11 @@ def f(): print(a) def test_set_cell(self): a = 12 - def f(): print(a) + def f(): return a c = f.__closure__ c[0].cell_contents = 9 self.assertEqual(c[0].cell_contents, 9) + self.assertEqual(f(), 9) del c[0].cell_contents try: c[0].cell_contents @@ -106,6 +107,12 @@ def f(): print(a) pass else: self.fail("shouldn't be able to read an empty cell") + try: + f() + except NameError: + pass + else: + self.fail("variable not deleted") def test___name__(self): self.assertEqual(self.b.__name__, 'b') From 447cc10bc39fdc23b5cf7c3922fe1d201166651f Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Tue, 6 Jun 2017 20:53:17 -0700 Subject: [PATCH 6/9] Updates tests. --- Lib/test/test_funcattrs.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 1b18649427529fc..42f5cc57273a9d1 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -100,6 +100,7 @@ def f(): return a c[0].cell_contents = 9 self.assertEqual(c[0].cell_contents, 9) self.assertEqual(f(), 9) + self.assertEqual(a, 9) del c[0].cell_contents try: c[0].cell_contents @@ -107,12 +108,8 @@ def f(): return a pass else: self.fail("shouldn't be able to read an empty cell") - try: + with self.assertRaises(NameError): f() - except NameError: - pass - else: - self.fail("variable not deleted") def test___name__(self): self.assertEqual(self.b.__name__, 'b') From b22b9453cfbef43b5e7717bbd2a06b784182bd3a Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Tue, 6 Jun 2017 21:35:12 -0700 Subject: [PATCH 7/9] Checks a value after deletion. --- Lib/test/test_funcattrs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 42f5cc57273a9d1..35fd657ec0ba567 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -110,6 +110,8 @@ def f(): return a self.fail("shouldn't be able to read an empty cell") with self.assertRaises(NameError): f() + with self.assertRaises(UnboundLocalError): + print(a) def test___name__(self): self.assertEqual(self.b.__name__, 'b') From f11ace77da7560ea6cd70431c01fb675473857fd Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 7 Jun 2017 17:21:11 -0700 Subject: [PATCH 8/9] removes trailing whitespace from docs. --- Doc/reference/datamodel.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 6867ff42b72572d..1f6af2b1dec4f91 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -532,9 +532,9 @@ Callable types dot-notation is used to get and set such attributes. *Note that the current implementation only supports function attributes on user-defined functions. Function attributes on built-in functions may be supported in the future. - + A cell object has the attribute ``cell_contents``. This can be used to get - the value of the cell, as well as set the value. + the value of the cell, as well as set the value. Additional information about a function's definition can be retrieved from its code object; see the description of internal types below. From bd5e329bf9c03f92c3edb3e13ff0c2efa4d5195f Mon Sep 17 00:00:00 2001 From: Lisa Roach Date: Wed, 7 Jun 2017 20:12:22 -0700 Subject: [PATCH 9/9] Adds back in *. --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 1f6af2b1dec4f91..4a8f295288eacd1 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -531,7 +531,7 @@ Callable types can be used, for example, to attach metadata to functions. Regular attribute dot-notation is used to get and set such attributes. *Note that the current implementation only supports function attributes on user-defined functions. - Function attributes on built-in functions may be supported in the future. + Function attributes on built-in functions may be supported in the future.* A cell object has the attribute ``cell_contents``. This can be used to get the value of the cell, as well as set the value.