On 25.11.15 08:39, Nick Coghlan wrote:
On 25 November 2015 at 07:33, Guido van Rossum email@example.com wrote:
Ooooh, that's probably really old code. I guess for the slots the reasoning is to save on slots. For the public functions, alas it will be hard to know if anyone is depending on it, even if it's undocumented. Perhaps add a deprecation warning to these if the value is NULL for one release cycle?
I did a quick scan for "PyObject_SetAttr", and it turns out PyObject_DelAttr is only a convenience macro for calling PyObject_SetAttr with NULL as the value argument. bltinmodule.c and ceval.c also both include direct calls to PyObject_SetAttr with "(PyObject *)NULL" as the value argument.
Investigating some of the uses that passed a variable as the value argument, one case is the weakref proxy implementation, which uses PyObject_SetAttr on the underlying object in its implementation of the setattr slot in the proxy.
So it looks to me like replicating the NULL-handling behaviour of the slots in the public Set APIs was intentional, and it's just the documentation of that detail that was missed (since most folks presumably use the Del convenience APIs instead).
I'm not sure. This looks rather as implementation detail to me. There cases found by you are the only cases in the core/stdlib that call PyObject_SetAttr with third argument is NULL. Tests are passed after replacing Set functions with Del functions in these cases and making Set* functions to reject value is NULL. 
Wouldn't be worth to deprecate deleting with Set functions? Neither other abstract Set APIs, not concrete Set APIs don't support deleting. Deleting with Set API can be unintentional and hide a bug.