Skip to content
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

Merged
merged 25 commits into from
Jul 9, 2024
Merged

Conversation

Linchin
Copy link
Contributor

@Linchin Linchin commented Jun 25, 2024

  • add an StreamGenerator class for all class methods stream() to return, instead of using the Python built-in generator.
  • use AsyncStreamGenerator for the async clients

@Linchin Linchin added the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Jun 25, 2024
@product-auto-label product-auto-label bot added size: m Pull request size is medium. api: firestore Issues related to the googleapis/python-firestore API. labels Jun 25, 2024
@@ -506,3 +566,122 @@ def get_partitions(
start_at = cursor

yield QueryPartition(self, start_at, None)


class Iterator(page_iterator.Iterator):
Copy link
Contributor

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

Copy link
Contributor Author

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.

@@ -506,3 +566,122 @@ def get_partitions(
start_at = cursor

yield QueryPartition(self, start_at, None)


class Iterator(page_iterator.Iterator):
Copy link
Contributor

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

Copy link
Contributor Author

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?

@product-auto-label product-auto-label bot added size: l Pull request size is large. and removed size: m Pull request size is medium. labels Jun 25, 2024
@product-auto-label product-auto-label bot added size: m Pull request size is medium. and removed size: l Pull request size is large. labels Jun 25, 2024
@product-auto-label product-auto-label bot added size: l Pull request size is large. and removed size: m Pull request size is medium. labels Jun 25, 2024
def __aiter__(self):
return self._generator

def __anext__(self):
Copy link
Contributor

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__()

Copy link
Contributor Author

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
Copy link
Contributor

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)

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 😂

Copy link
Contributor

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)

@Linchin Linchin marked this pull request as ready for review June 27, 2024 00:21
@Linchin Linchin added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 1, 2024
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 1, 2024
@Linchin Linchin added the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 1, 2024
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 1, 2024
Copy link
Contributor

@daniel-sanche daniel-sanche left a 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]`:
Copy link
Contributor

@daniel-sanche daniel-sanche Jul 2, 2024

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?

Copy link
Contributor Author

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]":
Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@Linchin Linchin added the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 3, 2024
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 3, 2024
@Linchin Linchin added kokoro:force-run Add this label to force Kokoro to re-run the tests. owlbot:run Add this label to trigger the Owlbot post processor. labels Jul 3, 2024
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 3, 2024
@yoshi-kokoro yoshi-kokoro removed kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Jul 3, 2024
@Linchin Linchin added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 3, 2024
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 3, 2024
@Linchin Linchin added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 8, 2024
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 8, 2024
@Linchin Linchin added kokoro:force-run Add this label to force Kokoro to re-run the tests. and removed kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Jul 8, 2024
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jul 9, 2024
@Linchin Linchin added the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 9, 2024
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Jul 9, 2024
@Linchin Linchin merged commit 3e5df35 into googleapis:main Jul 9, 2024
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: firestore Issues related to the googleapis/python-firestore API. size: l Pull request size is large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants