From df8d2cde63c865446468351f8f648e1c7bd45109 Mon Sep 17 00:00:00 2001 From: Pierre Glaser Date: Thu, 7 Feb 2019 20:36:48 +0100 Subject: bpo-35911: add cell constructor (GH-11771) Add a cell constructor, expose the cell type in the types module. --- Doc/library/types.rst | 8 +++++ Doc/reference/datamodel.rst | 4 ++- Lib/test/test_funcattrs.py | 9 +++++ Lib/types.py | 7 ++++ .../2019-02-06-17-50-59.bpo-35911.oiWE8.rst | 3 ++ Objects/cellobject.c | 42 +++++++++++++++++++++- 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-02-06-17-50-59.bpo-35911.oiWE8.rst diff --git a/Doc/library/types.rst b/Doc/library/types.rst index b19aa02..07c3a2e 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -136,6 +136,14 @@ Standard names are defined for the following types: The type for code objects such as returned by :func:`compile`. +.. data:: CellType + + The type for cell objects: such objects are used as containers for + a function's free variables. + + .. versionadded:: 3.8 + + .. data:: MethodType The type of methods of user-defined class instances. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 83e1d23..9961aee 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -539,7 +539,9 @@ Callable types 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. + code object; see the description of internal types below. The + :data:`cell ` type can be accessed in the :mod:`types` + module. Instance methods .. index:: diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 35fd657..11d68cc 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -83,6 +83,15 @@ class FunctionPropertiesTest(FuncAttrsTest): self.assertEqual(c[0].__class__.__name__, "cell") self.cannot_set_attr(f, "__closure__", c, AttributeError) + def test_cell_new(self): + cell_obj = types.CellType(1) + self.assertEqual(cell_obj.cell_contents, 1) + + cell_obj = types.CellType() + msg = "shouldn't be able to read an empty cell" + with self.assertRaises(ValueError, msg=msg): + cell_obj.cell_contents + def test_empty_cell(self): def f(): print(a) try: diff --git a/Lib/types.py b/Lib/types.py index ce4652f..53b588d 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -15,6 +15,13 @@ CodeType = type(_f.__code__) MappingProxyType = type(type.__dict__) SimpleNamespace = type(sys.implementation) +def _cell_factory(): + a = 1 + def f(): + nonlocal a + return f.__closure__[0] +CellType = type(_cell_factory()) + def _g(): yield 1 GeneratorType = type(_g()) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-02-06-17-50-59.bpo-35911.oiWE8.rst b/Misc/NEWS.d/next/Core and Builtins/2019-02-06-17-50-59.bpo-35911.oiWE8.rst new file mode 100644 index 0000000..458ccb4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-02-06-17-50-59.bpo-35911.oiWE8.rst @@ -0,0 +1,3 @@ +Enable the creation of cell objects by adding a ``cell.__new__`` method, and +expose the type ``cell`` in ``Lib/types.py`` under the name CellType. Patch by +Pierre Glaser. diff --git a/Objects/cellobject.c b/Objects/cellobject.c index 86bebb9..4e359f8 100644 --- a/Objects/cellobject.c +++ b/Objects/cellobject.c @@ -20,6 +20,37 @@ PyCell_New(PyObject *obj) return (PyObject *)op; } +PyDoc_STRVAR(cell_new_doc, +"cell([contents])\n" +"--\n" +"\n" +"Create a new cell object.\n" +"\n" +" contents\n" +" the contents of the cell. If not specified, the cell will be empty,\n" +" and \n further attempts to access its cell_contents attribute will\n" +" raise a ValueError."); + + +static PyObject * +cell_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *obj = NULL; + + if (!_PyArg_NoKeywords("cell", kwargs)) { + goto exit; + } + /* min = 0: we allow the cell to be empty */ + if (!PyArg_UnpackTuple(args, "cell", 0, 1, &obj)) { + goto exit; + } + return_value = PyCell_New(obj); + +exit: + return return_value; +} + PyObject * PyCell_Get(PyObject *op) { @@ -146,7 +177,7 @@ PyTypeObject PyCell_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ + cell_new_doc, /* tp_doc */ (traverseproc)cell_traverse, /* tp_traverse */ (inquiry)cell_clear, /* tp_clear */ cell_richcompare, /* tp_richcompare */ @@ -156,4 +187,13 @@ PyTypeObject PyCell_Type = { 0, /* tp_methods */ 0, /* tp_members */ cell_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)cell_new, /* tp_new */ + 0, /* tp_free */ }; -- cgit v0.12