-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: use generator for stream results #926
Conversation
google/cloud/firestore_v1/query.py
Outdated
@@ -506,3 +566,122 @@ def get_partitions( | |||
start_at = cursor | |||
|
|||
yield QueryPartition(self, start_at, None) | |||
|
|||
|
|||
class Iterator(page_iterator.Iterator): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why turn this into a page_iterator? Previously, it just returned a standard python generator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using datastore as a reference, but apparently as you noted below, I could just use a class to wrap this method.
google/cloud/firestore_v1/query.py
Outdated
@@ -506,3 +566,122 @@ def get_partitions( | |||
start_at = cursor | |||
|
|||
yield QueryPartition(self, start_at, None) | |||
|
|||
|
|||
class Iterator(page_iterator.Iterator): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also: I know this is early, but I'd probably prefer to give the iterator a more descriptive name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tentatively changed the name to StreamIterator
. Maybe we can find a better name, such as DocsIterator
?
def __aiter__(self): | ||
return self._generator | ||
|
||
def __anext__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you shouldn't need to implement the next functions, only iter/aiter. But I could be wrong
If we do need anext as well, you can probably just do
def __anext__(self):
return self._generator.__anext__()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks much more succinct! I think we will need to keep the next methods, because it's one of the methods that generator has.
try: | ||
return next(self._generator) | ||
except StopIteration: | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thought: previously, we were returning generator objects. If you decide to implement next, maybe we should turn this into a full generator and implement send and throw too to be safe?
(I don't think these would be used by current users, but it would keep everything consistent)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like I had better implement these, because send and throw are quite useful in coroutines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we were to go this route, should we rename the iterator to generator? Are we going too far on this 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I guess since the old type annotation was Generator, maybe we should make this into a full StreamGenerator
class to be consistent (not that the type annotations can be relied on)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple small comments about imports and docstrings, but LGTM!
@@ -150,13 +150,13 @@ def stream( | |||
to a system-specified value. | |||
|
|||
Returns: | |||
async_stream_iterator.AsyncStreamIterator: A generator of the query | |||
results. | |||
`AsyncStreamGenerator[DocumentSnapshot]`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need the ` quotes here? Do you know if devsite renders this well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I'm not super sure about this. I added the backticks because they are used in many other type annotations (for example). (A quick search shows the backtick makes sphinx create a link in the docs, but I'm not very clear about which documentation standard we are following.) Maybe we can clean this up along with other type annotations in a future PR. WDYT?
@@ -129,7 +129,7 @@ def stream( | |||
transaction: Optional[transaction.Transaction] = None, | |||
retry: Optional[retries.AsyncRetry] = gapic_v1.method.DEFAULT, | |||
timeout: Optional[float] = None, | |||
) -> async_stream_iterator.AsyncStreamIterator: | |||
) -> "AsyncStreamGenerator[DocumentSnapshot]": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need quotes here? It looks like AsyncStreamGenerator is imported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the quotes because [DocumentSnapshot]
part would cause an error, because AsyncStreamGenerator
isn't recognized as a generator in type hints. I wasn't able to find how to do it, so I resorted to using a string. I don't have a strong opinion on this. What do you think?
from google.cloud.firestore_v1.base_aggregation import ( | ||
AggregationResult, | ||
_query_response_to_result, | ||
BaseAggregationQuery, | ||
) | ||
|
||
from google.cloud.firestore_v1 import async_stream_iterator, transaction | ||
from google.cloud.firestore_v1.base_document import DocumentSnapshot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just used for type annotations? If so, you can put it in an if TYPE_CHECKING
block to avoid importing it in production
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
StreamGenerator
class for all class methodsstream()
to return, instead of using the Python built-in generator.AsyncStreamGenerator
for the async clients