(original) (raw)
Well, I think these are�mostly artifacts from old times, and usually passing None *should* be the same as omitting the argument. But check each case!
On Wednesday, January 15, 2014, Larry Hastings <larry@hastings.org> wrote:
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> 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:
The default value of every parameter is part of the function's signature--you can see them with inspect.signature() or inspect.getfullargspec().
- a parameter that is optional always has a default value, and
- this default value is visible to Python and is a Python value.
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): ...Assuming neither foo nor "any\_python\_value" have side effects, those two calls to foo() will be exactly the same in every way.
sig = inspect.signature(foo)
defaults = \[p.default for p in sig.parameters.values()\]
foo(\*defaults) == foo()
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 \*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:
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;
��� }
...
sig = inspect.signature(\_sha1.sha1)There's no value we could put in the signature for \_sha1.sha1 that would behave exactly the same as that NULL.
defaults = \[p.default for p in sig.parameters.values()\]
\_sha1.sha1(\*defaults) == \_sha1.sha1()
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.
--
--Guido van Rossum (on iPad)