Iter () does not work with datetime.now ()

A simple snippet in Python 3.6.1:

import datetime j = iter(datetime.datetime.now, None) next(j) 

returns:

 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

instead of typing the classic behavior of now() with each next() .

I saw similar code working in Python 3.3, am I missing something or has something changed in version 3.6.1?

+42
python datetime iterable
May 31 '17 at 11:27
source share
2 answers

This is definitely a bug that appeared in Python 3.6.0b1. The iter() implementation has recently switched to using _PyObject_FastCall() (for optimization, see issue 27128 ), and it should be this call that breaks this.

The same problem arises with other C classmethod , supported by an analysis of the Argument Clinic:

 >>> from asyncio import Task >>> Task.all_tasks() set() >>> next(iter(Task.all_tasks, None)) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

If you need a workaround, wrap the called functools.partial() object:

 from functools import partial j = iter(partial(datetime.datetime.now), None) 

I filed issue 30524 - iter (classmethod, sentinel), broken down into methods of the Argument Clinic class? with the Python project. The fix for this has landed and is part of 3.6.2rc1.

+42
May 31 '17 at 11:52 a.m.
source share
— -

I assume that you are using CPython and not another Python implementation. And I can reproduce the problem with CPython 3.6.1 (I don't have PyPy, Jython, IronPython, ... so I can't check them).

In this case, the offender is replacing PyObject_Call with _PyObject_CallNoArg in the C-equivalent of the callable_iterator.__next__ (your object is callable_iterator ).

PyObject_Call returns a new instance of datetime.datetime , and _PyObject_CallNoArg returns NULL (which is roughly equivalent to an exception in Python).

Dig a bit through CPython source code:

_PyObject_CallNoArg is just a macro for _PyObject_FastCall , which in turn is a macro for _PyObject_FastCallDict .

This _PyObject_FastCallDict function checks the type of the function ( C function or Python function or something else) and delegates to _PyCFunction_FastCallDict in this case, since datetime.now is a C function.

Since datetime.datetime.now has the METH_FASTCALL flag, it ends in the fourth case , but there _PyStack_UnpackDict returns NULL and the function is never called.

I will dwell on this and let the Python developers understand what is wrong there. @Martijn Pieters have already submitted a bug report and they will fix it (I just hope they fix it soon).

So, this is the error introduced in 3.6, and until it is fixed, you need to make sure that the method is not CFunction with the METH_FASTCALL flag. As a workaround, you can wrap it. In addition to the features mentioned in @Martijn Pieters, there is a simple one:

 def now(): return datetime.datetime.now() j = iter(now, None) next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999) 
+16
May 31 '17 at 12:22
source share



All Articles