bpo-30486: Allow setting cell value#1840
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1840 +/- ##
==========================================
- Coverage 83.68% 82.65% -1.03%
==========================================
Files 1371 1432 +61
Lines 347053 353644 +6591
==========================================
+ Hits 290428 292307 +1879
- Misses 56625 61337 +4712
Continue to review full report at Codecov.
|
rhettinger
left a comment
There was a problem hiding this comment.
Please add tests and documentation updates.
| cell_set_contents(PyCellObject *op, PyCellObject *obj) | ||
| { | ||
| Py_INCREF(obj); | ||
| PyCellObject *tmp_op = (PyCellObject *)(op->ob_ref); |
There was a problem hiding this comment.
I believe the 147 to 149 can be replace by a single invocation of the Py_SETREF macro. See +856 in Include/object.h.
There was a problem hiding this comment.
This is what I originally had in my first commit, but running make patchcheck showed this warning:
incompatible pointer types assigning to 'PyObject *' (aka 'struct _object *') from 'PyCellObject *'
Which is why I rewrote with the current lines. What do you think?
There was a problem hiding this comment.
What about
Py_XSETREF(op->ob_ref, (PyObject *)obj);
?
There was a problem hiding this comment.
You may have used the macro incorrectly. Did you try Py_SETREF(op->ob_ref, obj)?
Actually, you need Py_XSETREF (and Py_XINCREF), since obj can be NULL as @serhiy-storchaka pointed out.
There was a problem hiding this comment.
@serhiy-storchaka is right, the correct syntax is
Py_XSETREF(op->ob_ref, (PyObject *)obj);
But after looking at it more I obj should be a PyObject anyway and not a PyCellObject. I am new at C though so I could be wrong!
serhiy-storchaka
left a comment
There was a problem hiding this comment.
In addition to requested code changes, needed tests and an entry in Misc/NEWS (maybe even in What's New).
| int | ||
| cell_set_contents(PyCellObject *op, PyCellObject *obj) | ||
| { | ||
| Py_INCREF(obj); |
There was a problem hiding this comment.
obj can be NULL (when delete the attribute: del cell.cell_contents).
| int | ||
| cell_set_contents(PyCellObject *op, PyObject *obj) | ||
| { | ||
| if (obj == Py_None) |
There was a problem hiding this comment.
None is a legal value for the cell value.
| c = f.__closure__ | ||
| c[0].cell_contents = 9 | ||
| self.assertEqual(c[0].cell_contents, 9) | ||
| c[0].cell_contents = None |
There was a problem hiding this comment.
None is a legal value for the cell value.
You should test
del c[0].cell_contents
| | | attributes. | | | ||
| +-------------------------+-------------------------------+-----------+ | ||
| | :attr:`__closure__` | ``None`` or a tuple of cells | Read-only | | ||
| | :attr:`__closure__` | ``None`` or a tuple of cells | Writable | |
There was a problem hiding this comment.
The __closure__ attribute is not writable. The undocumented cell_contents attribute of __closure__ elements is writable.
|
|
||
| def test_set_cell(self): | ||
| a = 12 | ||
| def f(): print(a) |
There was a problem hiding this comment.
If you use def f(): return a instead, you could verify that calling f() does return the updated value.
There was a problem hiding this comment.
It's not a blocker to getting this accepted, btw, just nice to have ;-)
There was a problem hiding this comment.
That is a good idea!
| def f(): print(a) | ||
| c = f.__closure__ | ||
| c[0].cell_contents = 9 | ||
| self.assertEqual(c[0].cell_contents, 9) |
There was a problem hiding this comment.
It would be nice to check how setting and deleting cell_contents affects the local variable a.
| | | function's free variables. | | | ||
| | | The ``cell_contents`` | | | ||
| | | attribute can be used for | | | ||
| | | used for reading and writing | | |
There was a problem hiding this comment.
Duplicated "used for".
It may be better to describe the cell object somewhere else (for example in a footnote or in a separate paragraph following this table) than in a narrow table cell.
There was a problem hiding this comment.
It is kind of awkward trying to fit it in the table, I'll try to put it somewhere below.
| 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.* |
There was a problem hiding this comment.
Removed * (it needs for ending an emphasizing of a note).
There was a problem hiding this comment.
Should we remove the * in front of Note as well?
There was a problem hiding this comment.
Just restore removed * here.
There was a problem hiding this comment.
Oh oops, sorry I didn't realize I had deleted that!
| c = f.__closure__ | ||
| c[0].cell_contents = 9 | ||
| self.assertEqual(c[0].cell_contents, 9) | ||
| self.assertEqual(f(), 9) |
There was a problem hiding this comment.
Also check a directly.
self.assertEqual(a, 9)
| self.assertEqual(c[0].cell_contents, 9) | ||
| self.assertEqual(f(), 9) | ||
| del c[0].cell_contents | ||
| try: |
There was a problem hiding this comment.
I understand that you have copied an example from the above test, but this is too verbose. with self.assertRaises(ValueError): is much more compact.
| else: | ||
| self.fail("shouldn't be able to read an empty cell") | ||
| try: | ||
| with self.assertRaises(NameError): |
No description provided.