Procházet zdrojové kódy

Loader: work around Python 3.7 await headache caused by PEP 479

Generators can no longer raise StopIteration, and we are supposed to use "return" instead.  But, return with value inside generator is a syntax error in Python 2.

Fixes #513
rdb před 7 roky
rodič
revize
649372d333
1 změnil soubory, kde provedl 42 přidání a 20 odebrání
  1. 42 20
      direct/src/showbase/Loader.py

+ 42 - 20
direct/src/showbase/Loader.py

@@ -27,6 +27,42 @@ class Loader(DirectObject):
         # This indicates that this class behaves like a Future.
         _asyncio_future_blocking = False
 
+        class ResultAwaiter(object):
+            """Reinvents generators because of PEP 479, sigh.  See #513."""
+
+            __slots__ = 'requestList', 'index'
+
+            def __init__(self, requestList):
+                self.requestList = requestList
+                self.index = 0
+
+            def __await__(self):
+                return self
+
+            def __anext__(self):
+                if self.index >= len(self.requestList):
+                    raise StopAsyncIteration
+                return self
+
+            def __iter__(self):
+                return self
+
+            def __next__(self):
+                i = self.index
+                request = self.requestList[i]
+                if not request.done():
+                    return request
+
+                self.index = i + 1
+
+                result = request.result()
+                if isinstance(result, PandaNode):
+                    result = NodePath(result)
+
+                exc = StopIteration(result)
+                exc.value = result
+                raise exc
+
         def __init__(self, loader, numObjects, gotList, callback, extraArgs):
             self._loader = loader
             self.objects = [None] * numObjects
@@ -81,16 +117,14 @@ class Loader(DirectObject):
         def __await__(self):
             """ Returns a generator that raises StopIteration when the loading
             is complete.  This allows this class to be used with 'await'."""
+
             if self.requests:
                 self._asyncio_future_blocking = True
-                yield self
 
-            # This should be a simple return, but older versions of Python
-            # don't allow return statements with arguments.
-            result = self.result()
-            exc = StopIteration(result)
-            exc.value = result
-            raise exc
+            if self.gotList:
+                return self.ResultAwaiter([self])
+            else:
+                return self.ResultAwaiter(self.requestList)
 
         def __aiter__(self):
             """ This allows using `async for` to iterate asynchronously over
@@ -100,19 +134,7 @@ class Loader(DirectObject):
             requestList = self.requestList
             assert requestList is not None, "Request was cancelled."
 
-            class AsyncIter:
-                index = 0
-                def __anext__(self):
-                    if self.index < len(requestList):
-                        i = self.index
-                        self.index = i + 1
-                        return requestList[i]
-                    else:
-                        raise StopAsyncIteration
-
-            iter = AsyncIter()
-            iter.objects = self.objects
-            return iter
+            return self.ResultAwaiter(requestList)
 
     # special methods
     def __init__(self, base):