(original) (raw)

The trick you're missing is that \*any time\* you have an optional argument with a custom converter\[1\], PyArg\_ParseTuple \*only\* calls the converter function in the case that the user \*actually supplies some value\*. This is a basic property of an optional argument. Another property is that the c\_default is evaluated \*every time\*, as it is set \*before\* the call to PyArg\_ParseTuple. Are these the best ways to do things? Maybe not, but it's how they are.


Please do not use a custom converter for this case. It can't work. Please do what I outlined earlier (untested, somewhat verbose code follows):

static int
parse_time_t_arg(PyObject *arg, time_t *when)

{
if (arg == NULL || arg == Py\_None) {
\*when = time(NULL);
return 1;
}
if (\_PyTime\_ObjectToTime\_t(arg, when) == -1)
return 0;
return 1;
}

/\*\[clinic input\]
time.gmtime
seconds: object = None
\[clinic start generated code\]\*/

{

time_t when;

if (0 == parse_time_t_arg(seconds, &when))
return NULL;

...


\[1\] If you set a default value, or put it in brackets as Serhiy later recommends, it works the same.


On Sun, Jan 19, 2014 at 8:19 PM, Nikolaus Rath <Nikolaus@rath.org> wrote:
Larry Hastings <larry@hastings.org> writes:
\> On 01/18/2014 09:52 PM, Ryan Smith-Roberts wrote:
\>>
\>> I still advise you not to use this solution. time() is a system call
\>> on many operating systems, and so it can be a heavier operation than
\>> you'd think. Best to avoid it unless it's needed (on FreeBSD it
\>> seems to add about 15% overhead to localtime(), for instance).
\>>
\>
\> I agree. Converting to Argument Clinic should not cause a performance
\> regression. Please don't add new calls to time() for the sake of
\> making code more generic.
\>
\> A better choice would be to write a converter function in C, then use
\> a custom converter that called it. Nikolaus: Is that something you're
\> comfortable doing?

I think I'll need some help. I don't know how to handle the case where
the user is not passing anything.

Here's my attempt:

,----
| /\* C Converter for argument clinic
| If obj is NULL or Py\_None, return current time. Otherwise,
| convert Python object to time\_t.
| \*/
| static int
| PyObject\_to\_time\_t(PyObject \*obj, time\_t \*stamp)
| {
| if (obj == NULL || obj == Py\_None) {
| \*stamp = time(NULL);
| }
| else {
| if (\_PyTime\_ObjectToTime\_t(obj, stamp) == -1)
| return 0;
| }
| return 1;
| }
|
| /\*\[python input\]
| class time\_t\_converter(CConverter):
| type = 'time\_t'
| converter = 'PyObject\_to\_time\_t'
| default = None
| \[python start generated code\]\*/
| /\*\[python end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709\]\*/
|
|
| /\*\[clinic input\]
| time.gmtime
|
| seconds: time\_t
| /
|
| \[clinic start generated code\]\*/
\`----

but this results in the following code:

,----
| static PyObject \*
| time\_gmtime(PyModuleDef \*module, PyObject \*args)
| {
| PyObject \*return\_value = NULL;
| time\_t seconds;
|
| if (!PyArg\_ParseTuple(args,
| "|O&:gmtime",
| PyObject\_to\_time\_t, &seconds))
| goto exit;
| return\_value = time\_gmtime\_impl(module, seconds);
|
| exit:
| return return\_value;
| }
\`----

This works if the user calls time.gmtime(None), but it fails for
time.gmtime(). It seems that in that case my C converter function is
never called.

What's the trick that I'm missing?


Thanks!
-Nikolaus

\--
Encrypted emails preferred. PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6
02CF A9AD B7F8 AE4E 425C

»Time flies like an arrow, fruit flies like a Banana.«
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: https://mail.python.org/mailman/options/python-dev/rmsr%40lab.net