@@ -34,6 +34,7 @@ declare function __pyproxy_apply(
3434 kwargs_names : string [ ] ,
3535 num_kwargs : number ,
3636) : any ;
37+ declare function __pyproxyGen_Send ( ptr : number , arg : any ) : IteratorResult < any >
3738
3839// pyodide-skip
3940
@@ -298,6 +299,7 @@ function getPyProxyClass(flags: number) {
298299 [ HAS_LENGTH , PyLengthMethods ] ,
299300 [ HAS_SET , PySetItemMethods ] ,
300301 [ IS_CALLABLE , PyCallableMethods ] ,
302+ [ IS_ITERATOR , PyIteratorMethods ] ,
301303 ] ;
302304 for ( let [ feature_flag , methods ] of FLAG_TYPE_PAIRS ) {
303305 if ( flags & feature_flag ) {
@@ -1038,3 +1040,57 @@ function callPyObjectKwargs(ptrobj: number, jsargs: any[], kwargs: any) {
10381040function callPyObject ( ptrobj : number , jsargs : any ) {
10391041 return callPyObjectKwargs ( ptrobj , jsargs , { } ) ;
10401042}
1043+
1044+
1045+ /**
1046+ * A :js:class:`~pyodide.ffi.PyProxy` whose proxied Python object is an :term:`iterator`
1047+ * (i.e., has a :meth:`~generator.send` or :meth:`~iterator.__next__` method).
1048+ */
1049+ class PyIterator extends PyProxy {
1050+ /** @private */
1051+ static [ Symbol . hasInstance ] ( obj : any ) : obj is PyProxy {
1052+ return API . isPyProxy ( obj ) && ! ! ( _getFlags ( obj ) & IS_ITERATOR ) ;
1053+ }
1054+ }
1055+
1056+ interface PyIterator extends PyIteratorMethods { }
1057+
1058+ // Controlled by IS_ITERATOR, appears for any object with a __next__ or
1059+ // tp_iternext method.
1060+ class PyIteratorMethods {
1061+ /** @private */
1062+ [ Symbol . iterator ] ( ) {
1063+ return this ;
1064+ }
1065+ /**
1066+ * This translates to the Python code ``next(obj)``. Returns the next value of
1067+ * the generator. See the documentation for :js:meth:`Generator.next` The
1068+ * argument will be sent to the Python generator.
1069+ *
1070+ * This will be used implicitly by ``for(let x of proxy){}``.
1071+ *
1072+ * @param arg The value to send to the generator. The value will be assigned
1073+ * as a result of a yield expression.
1074+ * @returns An Object with two properties: ``done`` and ``value``. When the
1075+ * generator yields ``some_value``, ``next`` returns ``{done : false, value :
1076+ * some_value}``. When the generator raises a :py:exc:`StopIteration`
1077+ * exception, ``next`` returns ``{done : true, value : result_value}``.
1078+ */
1079+ next ( arg : any = undefined ) : IteratorResult < any , any > {
1080+ // Note: arg is optional, if arg is not supplied, it will be undefined
1081+ // which gets converted to "Py_None". This is as intended.
1082+ let result ;
1083+ let done ;
1084+ try {
1085+ Py_ENTER ( ) ;
1086+ result = __pyproxyGen_Send ( _getPtr ( this ) , arg ) ;
1087+ Py_EXIT ( ) ;
1088+ } catch ( e ) {
1089+ API . fatal_error ( e ) ;
1090+ }
1091+ if ( result === Module . error ) {
1092+ _pythonexc2js ( ) ;
1093+ }
1094+ return result ;
1095+ }
1096+ }
0 commit comments