On 01/15/2014 08:35 PM, Ryan Smith-Roberts wrote:
On Wed, Jan 15, 2014 at 7:57 PM, Ryan Smith-Roberts <rmsr@lab.net <mailto:rmsr@lab.net>> wrote:
socket.getservbyname(servicename[, protocolname]) -> socket.getservbyname(servicename, protocolname=None)
Here is a more complicated example, since the above does technically have an alternative fix:
sockobj.sendmsg(buffers[, ancdata[, flags[, address]]]) -> sockobj.sendmsg(buffers, ancdata=None, flags=0, address=None)
I feel like Ryan didn't sufficiently explain the problem. I'll elaborate. For functions implemented in Python, it's always true that: * a parameter that is optional always has a default value, and * this default value is visible to Python and is a Python value. The default value of every parameter is part of the function's signature--you can see them with inspect.signature() or inspect.getfullargspec(). A corollary of this: for every function implemented in Python, you can construct a call to it where you fill in every optional value with its published default value, and this is exactly equivalent to calling it without specifying those arguments: def foo(a=any_python_value): ... sig = inspect.signature(foo) defaults = [p.default for p in sig.parameters.values()] foo(*defaults) == foo() Assuming neither foo nor "any_python_value" have side effects, those two calls to foo() will be exactly the same in every way. But! Builtin functions implemented in C commonly have optional parameters whose default value is not only opaque to Python, it's not renderable as a Python value. That default value is NULL. Consider the first two paragraphs of SHA1_new() in Modules/sha1module.c: static PyObject * SHA1_new(PyObject *self, PyObject *args, PyObject *kwdict) { static char *kwlist[] = {"string", NULL}; SHA1object *new; PyObject *data_obj = NULL; Py_buffer buf; if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|O:new", kwlist, &data_obj)) { return NULL; } ... The "string" parameter is optional. Its value, if specified, is written to "data_obj". "data_obj" has a default value of NULL. There is no Python value you could pass in to this function that would result in "data_obj" being (redundantly) set to NULL. In Python SHA1_new is known as "_sha1.sha1". And: sig = inspect.signature(_sha1.sha1) defaults = [p.default for p in sig.parameters.values()] _sha1.sha1(*defaults) == _sha1.sha1() There's no value we could put in the signature for _sha1.sha1 that would behave exactly the same as that NULL. The ultimate goal of Argument Clinic is to provide introspection information for builtins. But we can't provide a default value to Python for the "string" parameter to _sha1.sha1() without changing the semantics of the function. We're stuck. Ryan's question, then, is "can we change a function like this so it accepts None?" My initial reaction is "no". That might be okay for _sha1.sha1(), but it'd be best to avoid. In the specific case of SHA1_new's "string" parameter, we could lie and claim that the default value is b''. Internally we could still use NULL as a default and get away with it. But this is only a happy coincidence. Many (most?) functions like this won't have a clever Python value we can trick you with. What else could we do? We could add a special value, let's call it sys.NULL, whose specific semantics are "turns into NULL when passed into builtins". This would solve the problem but it's really, really awful. The only other option I can see: don't convert SHA1_new() to use Argument Clinic, and don't provide introspection information for it either. Can you, gentle reader, suggest a better option? //arry/ p.s. Ryan's function signatures above suggest that he's converting code from using PyArg_ParseTuple into using PyArg_ParseTupleAndKeywords. I don't think he's *actually* doing that, and if I saw that in patches submitted to me I would ask that it be fixed.