From f8a50b433f312febda6c1dec222ed18039067f81 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Oct 2026 16:58:59 -0700 Subject: [PATCH 1/2] fix(firestore): add generic typing for DocumentSnapshot --- .../google/cloud/firestore_v1/async_client.py | 6 ++--- .../cloud/firestore_v1/async_collection.py | 4 ++-- .../cloud/firestore_v1/async_document.py | 2 +- .../google/cloud/firestore_v1/async_query.py | 13 +++++++---- .../cloud/firestore_v1/async_transaction.py | 7 ++++-- .../cloud/firestore_v1/async_vector_query.py | 9 +++++--- .../cloud/firestore_v1/base_document.py | 23 ++++++++++++++----- .../google/cloud/firestore_v1/client.py | 6 ++--- .../google/cloud/firestore_v1/collection.py | 4 ++-- .../google/cloud/firestore_v1/document.py | 2 +- .../google/cloud/firestore_v1/query.py | 11 +++++---- .../google/cloud/firestore_v1/transaction.py | 7 ++++-- .../google/cloud/firestore_v1/vector_query.py | 7 +++--- .../tests/unit/v1/test_base_document.py | 18 +++++++++++++++ 14 files changed, 81 insertions(+), 38 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_client.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_client.py index 3167335e0385..5ae112956f9b 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_client.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_client.py @@ -247,7 +247,7 @@ async def get_all( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> AsyncGenerator[DocumentSnapshot, Any]: + ) -> AsyncGenerator[DocumentSnapshot[AsyncDocumentReference], Any]: """Retrieve a batch of documents. .. note:: @@ -385,13 +385,13 @@ async def _recursive_delete( num_deleted: int = 0 if isinstance(reference, AsyncCollectionReference): - chunk: List[DocumentSnapshot] + chunk: List[DocumentSnapshot[AsyncDocumentReference]] async for chunk in ( reference.recursive() .select([FieldPath.document_id()]) ._chunkify(chunk_size) ): - doc_snap: DocumentSnapshot + doc_snap: DocumentSnapshot[AsyncDocumentReference] for doc_snap in chunk: num_deleted += 1 bulk_writer.delete(doc_snap.reference) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_collection.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_collection.py index e706733c53aa..732078a0833a 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_collection.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_collection.py @@ -207,7 +207,7 @@ async def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[AsyncDocumentReference]]: """Read the documents in this collection. This sends a ``RunQuery`` RPC and returns a list of documents @@ -254,7 +254,7 @@ def stream( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> AsyncStreamGenerator[DocumentSnapshot]: + ) -> AsyncStreamGenerator[DocumentSnapshot[AsyncDocumentReference]]: """Read the documents in this collection. This sends a ``RunQuery`` RPC and then returns a generator which diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_document.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_document.py index 85d4dc3bae19..8c2a15e44d09 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_document.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_document.py @@ -333,7 +333,7 @@ async def get( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> DocumentSnapshot: + ) -> DocumentSnapshot[AsyncDocumentReference]: """Retrieve a snapshot of the current document. See :meth:`~google.cloud.firestore_v1.base_client.BaseClient.field_path` for diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_query.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_query.py index 7f1ca7cab364..0495d46cdb8b 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_query.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_query.py @@ -55,6 +55,7 @@ import google.cloud.firestore_v1.types.query_profile as query_profile_pb # Types needed only for Type Hints + from google.cloud.firestore_v1.async_document import AsyncDocumentReference from google.cloud.firestore_v1.async_transaction import AsyncTransaction from google.cloud.firestore_v1.base_document import DocumentSnapshot from google.cloud.firestore_v1.base_vector_query import DistanceMeasure @@ -152,11 +153,11 @@ def __init__( async def _chunkify( self, chunk_size: int - ) -> AsyncGenerator[List[DocumentSnapshot], None]: + ) -> AsyncGenerator[List[DocumentSnapshot[AsyncDocumentReference]], None]: max_to_return: Optional[int] = self._limit num_returned: int = 0 original: AsyncQuery = self._copy() - last_document: Optional[DocumentSnapshot] = None + last_document: Optional[DocumentSnapshot[AsyncDocumentReference]] = None while True: # Optionally trim the `chunk_size` down to honor a previously @@ -196,7 +197,7 @@ async def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[AsyncDocumentReference]]: """Read the documents in the collection that match this query. This sends a ``RunQuery`` RPC and returns a list of documents @@ -356,7 +357,9 @@ async def _make_stream( timeout: Optional[float] = None, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> AsyncGenerator[DocumentSnapshot | query_profile_pb.ExplainMetrics, Any]: + ) -> AsyncGenerator[ + DocumentSnapshot[AsyncDocumentReference] | query_profile_pb.ExplainMetrics, Any + ]: """Internal method for stream(). Read the documents in the collection that match this query. @@ -438,7 +441,7 @@ def stream( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> AsyncStreamGenerator[DocumentSnapshot]: + ) -> AsyncStreamGenerator[DocumentSnapshot[AsyncDocumentReference]]: """Read the documents in the collection that match this query. This sends a ``RunQuery`` RPC and then returns a generator which diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_transaction.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_transaction.py index c1724b69633e..4971c1ea023e 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_transaction.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_transaction.py @@ -174,7 +174,7 @@ async def get_all( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> AsyncGenerator[DocumentSnapshot, Any]: + ) -> AsyncGenerator[DocumentSnapshot[AsyncDocumentReference], Any]: """Retrieves multiple documents from Firestore. Args: @@ -206,7 +206,10 @@ async def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> AsyncGenerator[DocumentSnapshot, Any] | AsyncStreamGenerator[DocumentSnapshot]: + ) -> ( + AsyncGenerator[DocumentSnapshot[AsyncDocumentReference], Any] + | AsyncStreamGenerator[DocumentSnapshot[AsyncDocumentReference]] + ): """ Retrieve a document or a query result from the database. diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_vector_query.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_vector_query.py index 4158e874bb61..0db958ac9e83 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/async_vector_query.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/async_vector_query.py @@ -32,6 +32,7 @@ if TYPE_CHECKING: # pragma: NO COVER import google.cloud.firestore_v1.types.query_profile as query_profile_pb from google.cloud.firestore_v1 import transaction + from google.cloud.firestore_v1.async_document import AsyncDocumentReference from google.cloud.firestore_v1.base_document import DocumentSnapshot from google.cloud.firestore_v1.query_profile import ExplainMetrics, ExplainOptions @@ -58,7 +59,7 @@ async def get( timeout: Optional[float] = None, *, explain_options: Optional[ExplainOptions] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[AsyncDocumentReference]]: """Runs the vector query. This sends a ``RunQuery`` RPC and returns a list of document messages. @@ -109,7 +110,9 @@ async def _make_stream( retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, explain_options: Optional[ExplainOptions] = None, - ) -> AsyncGenerator[DocumentSnapshot | query_profile_pb.ExplainMetrics, Any]: + ) -> AsyncGenerator[ + DocumentSnapshot[AsyncDocumentReference] | query_profile_pb.ExplainMetrics, Any + ]: """Internal method for stream(). Read the documents in the collection that match this query. @@ -177,7 +180,7 @@ def stream( timeout: Optional[float] = None, *, explain_options: Optional[ExplainOptions] = None, - ) -> AsyncStreamGenerator[DocumentSnapshot]: + ) -> AsyncStreamGenerator[DocumentSnapshot[AsyncDocumentReference]]: """Reads the documents in the collection that match this query. This sends a ``RunQuery`` RPC and then returns an iterator which diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py index 92d8daa21fd6..884356f873e9 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py @@ -22,9 +22,11 @@ Any, Awaitable, Dict, + Generic, Iterable, Optional, Tuple, + TypeVar, Union, ) @@ -41,6 +43,9 @@ from google.cloud.firestore_v1.types import Document, firestore, write +DocRefType = TypeVar("DocRefType", bound="BaseDocumentReference") + + class BaseDocumentReference(object): """A reference to a document in a Firestore database. @@ -359,7 +364,7 @@ def on_snapshot(self, callback): raise NotImplementedError -class DocumentSnapshot(object): +class DocumentSnapshot(Generic[DocRefType]): """A snapshot of document data in a Firestore database. This represents data retrieved at a specific time and may not contain @@ -371,7 +376,7 @@ class DocumentSnapshot(object): :meth:`~google.cloud.DocumentReference.get`. Args: - reference (:class:`~google.cloud.firestore_v1.document.DocumentReference`): + reference (Union[:class:`~google.cloud.firestore_v1.document.DocumentReference`, :class:`~google.cloud.firestore_v1.async_document.AsyncDocumentReference`]): A document reference corresponding to the document that contains the data in this snapshot. data (Dict[str, Any]): @@ -388,9 +393,15 @@ class DocumentSnapshot(object): """ def __init__( - self, reference, data, exists, read_time, create_time, update_time + self, + reference: DocRefType, + data, + exists, + read_time, + create_time, + update_time, ) -> None: - self._reference = reference + self._reference: DocRefType = reference # We want immutable data, so callers can't modify this value # out from under us. self._data = copy.deepcopy(data) @@ -439,11 +450,11 @@ def id(self) -> str: return self._reference.id @property - def reference(self) -> BaseDocumentReference: + def reference(self) -> DocRefType: """Document reference corresponding to document that owns this data. Returns: - :class:`~google.cloud.firestore_v1.document.DocumentReference`: + Union[:class:`~google.cloud.firestore_v1.document.DocumentReference`, :class:`~google.cloud.firestore_v1.async_document.AsyncDocumentReference`]: A document reference corresponding to this document. """ return self._reference diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py index e29d07cb09ac..9f35fb3f59d5 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/client.py @@ -213,7 +213,7 @@ def get_all( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> Generator[DocumentSnapshot, Any, None]: + ) -> Generator[DocumentSnapshot[DocumentReference], Any, None]: """Retrieve a batch of documents. .. note:: @@ -353,13 +353,13 @@ def _recursive_delete( num_deleted: int = 0 if isinstance(reference, CollectionReference): - chunk: List[DocumentSnapshot] + chunk: List[DocumentSnapshot[DocumentReference]] for chunk in ( reference.recursive() .select([FieldPath.document_id()]) ._chunkify(chunk_size) ): - doc_snap: DocumentSnapshot + doc_snap: DocumentSnapshot[DocumentReference] for doc_snap in chunk: num_deleted += 1 bulk_writer.delete(doc_snap.reference) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/collection.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/collection.py index fb7aebda0d31..78c1d1fee9fe 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/collection.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/collection.py @@ -202,7 +202,7 @@ def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[DocumentReference]]: """Read the documents in this collection. This sends a ``RunQuery`` RPC and returns a list of documents @@ -249,7 +249,7 @@ def stream( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> StreamGenerator[DocumentSnapshot]: + ) -> StreamGenerator[DocumentSnapshot[DocumentReference]]: """Read the documents in this collection. This sends a ``RunQuery`` RPC and then returns an iterator which diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/document.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/document.py index 11065ee8cb03..30c91974ab3a 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/document.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/document.py @@ -369,7 +369,7 @@ def get( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> DocumentSnapshot: + ) -> DocumentSnapshot[DocumentReference]: """Retrieve a snapshot of the current document. See :meth:`~google.cloud.firestore_v1.base_client.BaseClient.field_path` for diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/query.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/query.py index 17b874129b87..89b8fdd9e2bc 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/query.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/query.py @@ -59,6 +59,7 @@ import datetime from google.cloud.firestore_v1.base_vector_query import DistanceMeasure + from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1.field_path import FieldPath from google.cloud.firestore_v1.query_profile import ExplainMetrics, ExplainOptions @@ -155,7 +156,7 @@ def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[DocumentReference]]: """Read the documents in the collection that match this query. This sends a ``RunQuery`` RPC and returns a list of documents @@ -221,11 +222,11 @@ def get( def _chunkify( self, chunk_size: int - ) -> Generator[List[DocumentSnapshot], None, None]: + ) -> Generator[List[DocumentSnapshot[DocumentReference]], None, None]: max_to_return: Optional[int] = self._limit num_returned: int = 0 original: Query = self._copy() - last_document: Optional[DocumentSnapshot] = None + last_document: Optional[DocumentSnapshot[DocumentReference]] = None while True: # Optionally trim the `chunk_size` down to honor a previously @@ -372,7 +373,7 @@ def _make_stream( timeout: float | None = None, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> Generator[DocumentSnapshot, Any, Optional[ExplainMetrics]]: + ) -> Generator[DocumentSnapshot[DocumentReference], Any, Optional[ExplainMetrics]]: """Internal method for stream(). Read the documents in the collection that match this query. @@ -474,7 +475,7 @@ def stream( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> StreamGenerator[DocumentSnapshot]: + ) -> StreamGenerator[DocumentSnapshot[DocumentReference]]: """Read the documents in the collection that match this query. This sends a ``RunQuery`` RPC and then returns a generator which diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/transaction.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/transaction.py index 88d9ab77f5da..1d2cde079b48 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/transaction.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/transaction.py @@ -159,7 +159,7 @@ def get_all( timeout: float | None = None, *, read_time: datetime.datetime | None = None, - ) -> Generator[DocumentSnapshot, Any, None]: + ) -> Generator[DocumentSnapshot[DocumentReference], Any, None]: """Retrieves multiple documents from Firestore. Args: @@ -191,7 +191,10 @@ def get( *, explain_options: Optional[ExplainOptions] = None, read_time: Optional[datetime.datetime] = None, - ) -> StreamGenerator[DocumentSnapshot] | Generator[DocumentSnapshot, Any, None]: + ) -> ( + StreamGenerator[DocumentSnapshot[DocumentReference]] + | Generator[DocumentSnapshot[DocumentReference], Any, None] + ): """Retrieve a document or a query result from the database. Args: diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/vector_query.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/vector_query.py index 288b0fcc9d94..9d26ac0ae21e 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/vector_query.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/vector_query.py @@ -34,6 +34,7 @@ if TYPE_CHECKING: # pragma: NO COVER from google.cloud.firestore_v1 import transaction from google.cloud.firestore_v1.base_document import DocumentSnapshot + from google.cloud.firestore_v1.document import DocumentReference from google.cloud.firestore_v1.query_profile import ExplainMetrics, ExplainOptions @@ -60,7 +61,7 @@ def get( timeout: Optional[float] = None, *, explain_options: Optional[ExplainOptions] = None, - ) -> QueryResultsList[DocumentSnapshot]: + ) -> QueryResultsList[DocumentSnapshot[DocumentReference]]: """Runs the vector query. This sends a ``RunQuery`` RPC and returns a list of document messages. @@ -124,7 +125,7 @@ def _make_stream( retry: retries.Retry | object | None = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, explain_options: Optional[ExplainOptions] = None, - ) -> Generator[DocumentSnapshot, Any, Optional[ExplainMetrics]]: + ) -> Generator[DocumentSnapshot[DocumentReference], Any, Optional[ExplainMetrics]]: """Reads the documents in the collection that match this query. This sends a ``RunQuery`` RPC and then returns a generator which @@ -195,7 +196,7 @@ def stream( timeout: Optional[float] = None, *, explain_options: Optional[ExplainOptions] = None, - ) -> StreamGenerator[DocumentSnapshot]: + ) -> StreamGenerator[DocumentSnapshot[DocumentReference]]: """Reads the documents in the collection that match this query. This sends a ``RunQuery`` RPC and then returns a generator which diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py b/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py index b2dff117cd73..0812ca3beace 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py @@ -247,6 +247,24 @@ def test_documentsnapshot_constructor(): assert snapshot.update_time is mock.sentinel.update_time +def test_documentsnapshot_generic_typing(): + from typing import get_args, get_origin + + from google.cloud.firestore_v1.async_document import AsyncDocumentReference + from google.cloud.firestore_v1.document import ( + DocumentReference, + DocumentSnapshot, + ) + + sync_snapshot_type = DocumentSnapshot[DocumentReference] + assert get_origin(sync_snapshot_type) is DocumentSnapshot + assert get_args(sync_snapshot_type) == (DocumentReference,) + + async_snapshot_type = DocumentSnapshot[AsyncDocumentReference] + assert get_origin(async_snapshot_type) is DocumentSnapshot + assert get_args(async_snapshot_type) == (AsyncDocumentReference,) + + def test_documentsnapshot___eq___other_type(): snapshot = _make_w_ref() other = object() From be591cb601704fe2251a6b490b1cf1544e2afb49 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Oct 2026 18:04:17 -0700 Subject: [PATCH 2/2] address PR comments --- .../google/cloud/firestore_v1/base_document.py | 6 +++--- .../tests/unit/v1/test_base_document.py | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py index 884356f873e9..83e829731832 100644 --- a/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py +++ b/packages/google-cloud-firestore/google/cloud/firestore_v1/base_document.py @@ -43,9 +43,6 @@ from google.cloud.firestore_v1.types import Document, firestore, write -DocRefType = TypeVar("DocRefType", bound="BaseDocumentReference") - - class BaseDocumentReference(object): """A reference to a document in a Firestore database. @@ -364,6 +361,9 @@ def on_snapshot(self, callback): raise NotImplementedError +DocRefType = TypeVar("DocRefType", bound=BaseDocumentReference) + + class DocumentSnapshot(Generic[DocRefType]): """A snapshot of document data in a Firestore database. diff --git a/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py b/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py index 0812ca3beace..d22a37b07761 100644 --- a/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py +++ b/packages/google-cloud-firestore/tests/unit/v1/test_base_document.py @@ -251,11 +251,17 @@ def test_documentsnapshot_generic_typing(): from typing import get_args, get_origin from google.cloud.firestore_v1.async_document import AsyncDocumentReference + from google.cloud.firestore_v1.base_document import ( + BaseDocumentReference, + DocRefType, + ) from google.cloud.firestore_v1.document import ( DocumentReference, DocumentSnapshot, ) + assert DocRefType.__bound__ is BaseDocumentReference + sync_snapshot_type = DocumentSnapshot[DocumentReference] assert get_origin(sync_snapshot_type) is DocumentSnapshot assert get_args(sync_snapshot_type) == (DocumentReference,)