You can rewrite your function to act as a generator by simply yielding single file paths.
Untested:
def listAllFiles(self):
result = []
page_token = None
while True:
try:
param = {"q" : "trashed=false", "orderBy": "createdTime"}
if page_token:
param['pageToken'] = page_token
files = self.service.files().list(**param).execute()
# call future to load the next bunch of files here!
for f in files["files"]:
yield f
page_token = files.get('nextPageToken')
if not page_token: break
except errors.HttpError as error:
print('An error occurred:', error)
break
If you do not further parallelize use chapelo's answer instead. Yielding the list of all available files will allow the coroutine to continue and thus, begin to fetch the next list of files concurrently.
Preloading the next bunch with futures
Now, you are still not loading the next bunch of files concurrently.
For this, as mentioned in the code above, you could execute a future to already gather the next list of files concurrently.
When your yielded item is consumed (and your function continues to execute) you look into your future to see whether the result is already there. If not, you have to wait (as before) until the result arrives.
As I don't have your code available I can not say whether this code works (or is even syntactically correct), but you can use it as a starting point:
import concurrent.futures
def load_next_page(self, page_token=None):
param = {"q" : "trashed=false", "orderBy": "createdTime"}
if page_token:
param['pageToken'] = page_token
result = None
try:
files = self.service.files().list(**param).execute()
result = (files.get('nextPageToken'), files["files"])
except errors.HttpError as error:
print('An error occurred:', error)
return result
def listAllFiles(self):
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(self.load_next_page, 60)
while future:
try:
result = future.result()
future = None
if not result:
break
(next_page_token, files) = result
except Exception as error:
print('An error occured:', error)
break
if next_page_token:
future = executor.submit(self.load_next_page, next_page_token, 60)
# yield from files
for f in files:
yield f
Producer/Consumer parallelization with Queues
Another option, as also mentioned in the comments, is to use a Queue. You can modify your function to return a queue which is filled by a thread spawned by your function. This should faster than only preloading the next list, but also yields a higher implementation overhead.
I, personally, would recommend to go with the future path -- if the performance is adequate.