1313from grpc .aio import Channel , UnaryStreamCall
1414
1515from .. import aio
16+ from ... import LOG
1617from ..._gen .com .daml .ledger .api import v1 as lapipb
1718from ..._gen .com .daml .ledger .api .v1 import admin as lapiadminpb
1819from ...damlast .daml_lf_1 import PackageRef , TypeConName
1920from ...damlast .util import is_match
2021from ...prim import LEDGER_STRING_REGEX , ContractData , ContractId , Party
2122from ...query import Filter , Queries , Query , parse_query
22- from .._offsets import UNTIL_END , LedgerOffsetRange , from_offset_until_forever
23+ from .._offsets import END , UNTIL_END , End , LedgerOffsetRange , from_offset_until_forever
2324from ..api_types import ArchiveEvent , Boundary , Command , CreateEvent , ExerciseResponse , PartyInfo
2425from ..config import Config
2526from ..config .access import PropertyBasedAccessConfig
@@ -455,7 +456,12 @@ def _submit_and_wait_request(
455456 # region Read API
456457
457458 def query (
458- self , __template_id : Union [str , TypeConName ] = "*" , __query : Query = None
459+ self ,
460+ __template_id : Union [str , TypeConName ] = "*" ,
461+ __query : Query = None ,
462+ * ,
463+ begin_offset : Optional [str ] = None ,
464+ end_offset : Optional [str ] = None ,
459465 ) -> "QueryStream" :
460466 """
461467 Return the create events from the active contract set service as a stream.
@@ -468,12 +474,25 @@ def query(
468474 The name of the template for which to fetch contracts.
469475 :param __query:
470476 A filter to apply to the set of returned contracts.
471- """
477+ :param begin_offset:
478+ The starting offset at which to read an active contract set. If ``None``, contracts
479+ are read from the beginning, and using the Active Contract Set Service instead of the
480+ Transaction Service.
481+ :param end_offset:
482+ The ending offset. If ``None``, contracts are read until the end of the stream.
483+ In order to read indefinitely, use :meth:`stream` instead.
484+ """
485+ offset = LedgerOffsetRange (begin_offset , end_offset if end_offset is not None else END )
472486 return QueryStream (
473- self , parse_query ({__template_id : __query }, server_side_filters = False ), UNTIL_END
487+ self , parse_query ({__template_id : __query }, server_side_filters = False ), offset
474488 )
475489
476- def query_many (self , * queries : Queries ) -> "QueryStream" :
490+ def query_many (
491+ self ,
492+ * queries : Queries ,
493+ begin_offset : Optional [str ] = None ,
494+ end_offset : Optional [str ] = None ,
495+ ) -> "QueryStream" :
477496 """
478497 Return the create events from the active contract set service as a stream.
479498
@@ -483,8 +502,16 @@ def query_many(self, *queries: Queries) -> "QueryStream":
483502
484503 :param queries:
485504 A map of template IDs to filter to apply to the set of returned contracts.
505+ :param begin_offset:
506+ The starting offset at which to read an active contract set. If ``None``, contracts
507+ are read from the beginning, and using the Active Contract Set Service instead of the
508+ Transaction Service.
509+ :param end_offset:
510+ The ending offset. If ``None``, contracts are read until the end of the stream.
511+ In order to read indefinitely, use :meth:`stream_many` instead.
486512 """
487- return QueryStream (self , parse_query (* queries , server_side_filters = False ), UNTIL_END )
513+ offset = LedgerOffsetRange (begin_offset , end_offset if end_offset is not None else END )
514+ return QueryStream (self , parse_query (* queries , server_side_filters = False ), offset )
488515
489516 def stream (
490517 self ,
@@ -643,8 +670,10 @@ async def items(self):
643670 filters_by_party = {party : filters for party in self .conn .config .access .read_as }
644671 tx_filter_pb = lapipb .TransactionFilter (filters_by_party = filters_by_party )
645672
646- offset = None
647- if self ._offset_range .begin is None :
673+ offset = self ._offset_range .begin
674+ if offset :
675+ log .debug ("Skipped reading from the ACS because begin offset is %r" , offset )
676+ else :
648677 # when starting from the beginning of the ledger, the Active Contract Set service
649678 # lets us catch up more quickly than having to parse every create/archive event
650679 # ourselves
@@ -658,31 +687,30 @@ async def items(self):
658687 else :
659688 warnings .warn (f"Received an unknown event: { event } " , ProtocolWarning )
660689 yield event
661- else :
662- log .debug (
663- "Skipped reading from the ACS because begin offset is %r" ,
664- self ._offset_range .begin ,
665- )
666690
667- if self ._offset_range != UNTIL_END :
668- # now start returning events as they come off the transaction stream; note this
669- # stream will never naturally close, so it's on the caller to call close() or to
670- # otherwise exit our current context
671- log .debug ("Reading a transaction stream: %s" , self ._offset_range )
672- async for event in self ._tx_events (tx_filter_pb , offset ):
673- if isinstance (event , CreateEvent ):
674- await self ._emit_create (event )
675- elif isinstance (event , ArchiveEvent ):
676- await self ._emit_archive (event )
677- elif isinstance (event , Boundary ):
678- await self ._emit_boundary (event )
679- else :
680- warnings .warn (f"Received an unknown event: { event } " , ProtocolWarning )
681- yield event
682- else :
683- log .debug (
684- "Not reading from transaction stream because we were only asked for a snapshot."
685- )
691+ # when reading from the Active Contract Set service, if we're supposed to stop
692+ # at "the end", then the Active Contract Set data is all that we'll return
693+ if self ._offset_range .end == END :
694+ log .debug (
695+ "Not reading from transaction stream because we were only asked for a snapshot."
696+ )
697+ return
698+
699+ # now start returning events as they come off the transaction stream; note this
700+ # stream will never naturally close, so it's on the caller to call close() or to
701+ # otherwise exit our current context
702+ log .debug ("Reading a transaction stream: %s" , self ._offset_range )
703+ async for event in self ._tx_events (tx_filter_pb , offset , self ._offset_range .end ):
704+ log .debug ("Received an event: %s" , event )
705+ if isinstance (event , CreateEvent ):
706+ await self ._emit_create (event )
707+ elif isinstance (event , ArchiveEvent ):
708+ await self ._emit_archive (event )
709+ elif isinstance (event , Boundary ):
710+ await self ._emit_boundary (event )
711+ else :
712+ warnings .warn (f"Received an unknown event: { event } " , ProtocolWarning )
713+ yield event
686714
687715 async def _acs_events (
688716 self , filter_pb : lapipb .TransactionFilter
@@ -696,23 +724,33 @@ async def _acs_events(
696724
697725 offset = None
698726 async for response in response_stream :
727+ LOG .debug (
728+ "ACS start (offset %r, %d event(s))" ,
729+ response .offset ,
730+ len (response .active_contracts ),
731+ )
699732 for event in response .active_contracts :
700733 c_evt = await self .conn .codec .decode_created_event (event )
701734 if self ._is_match (c_evt ):
702735 yield c_evt
703736 # for ActiveContractSetResponse messages, only the last offset is actually relevant
737+ LOG .debug ("ACS end (offset %r)" , response .offset )
704738 offset = response .offset
705739 yield Boundary (offset )
706740
707741 async def _tx_events (
708- self , filter_pb : lapipb .TransactionFilter , begin_offset : Optional [str ]
742+ self ,
743+ filter_pb : lapipb .TransactionFilter ,
744+ begin_offset : Optional [str ],
745+ end_offset : "Union[None, str, End]" ,
709746 ) -> AsyncIterable [Union [CreateEvent , ArchiveEvent , Boundary ]]:
710747 stub = lapipb .TransactionServiceStub (self .conn .channel )
711748
712749 request = lapipb .GetTransactionsRequest (
713750 ledger_id = self .conn .config .access .ledger_id ,
714751 filter = filter_pb ,
715752 begin = self .conn .codec .encode_begin_offset (begin_offset ),
753+ end = self .conn .codec .encode_end_offset (end_offset ),
716754 )
717755
718756 self ._response_stream = response_stream = stub .GetTransactions (request )
0 commit comments