@@ -177,10 +177,9 @@ def _slice(self, index: slice) -> Sequence[Event]:
177177 if provider is not None :
178178 if start >= 0 :
179179 return provider .events_from (start )
180- if - start <= min (
181- self ._transcript ._resident_tail , len (self ._transcript ._events )
182- ):
183- return self ._transcript ._events [start :]
180+ window = self ._transcript ._trailing_window (- start )
181+ if window is not None :
182+ return window
184183 return provider .recent_events (- start )
185184 return self ._materialize ()[index ]
186185
@@ -287,10 +286,10 @@ def recent_events(self, n: int | None = None) -> Sequence[Event]:
287286 "Full transcript history is not available from this Transcript"
288287 )
289288 return transcript ._events if n is None else transcript ._events [- n :]
290- if n is not None and n <= min (
291- transcript . _resident_tail , len ( transcript ._events )
292- ) :
293- return transcript . _events [ - n :]
289+ if n is not None :
290+ window = transcript ._trailing_window ( n )
291+ if window is not None :
292+ return window
294293 return transcript ._history_provider .recent_events (n )
295294
296295 def events_from (self , start : int , limit : int | None = None ) -> Sequence [Event ]:
@@ -324,23 +323,21 @@ def events_from(self, start: int, limit: int | None = None) -> Sequence[Event]:
324323 if start >= count :
325324 return []
326325 if not transcript ._events_truncated :
327- # nothing evicted: resident events ARE the logical history
328- events : Sequence [Event ] = transcript ._events [start :]
329- return events [:limit ] if limit is not None else events
326+ # nothing evicted: resident events ARE the logical history. Slice
327+ # page-bounded in one step — copying the whole tail and then
328+ # trimming it would be O(remaining history) per page on large
329+ # transcripts.
330+ end = count if limit is None else start + limit
331+ return transcript ._events [start :end ]
330332 # Once events have been evicted, resident events are NOT a contiguous
331- # suffix of the logical history: pinned and pending events survive
332- # eviction at their insertion positions (eg. a SampleInitEvent ahead
333- # of the resident tail), so a logical offset can't be mapped into
334- # ``_events`` by suffix arithmetic. The exception is the trailing
335- # window ``recent_events`` also relies on: the newest
336- # ``resident_tail`` logical events are always resident and always the
337- # last elements of ``_events`` (eviction removes only *older*
338- # evictable events), so a read confined to that window is a memory
339- # slice. Anything earlier must come from the provider.
340- n = count - start
341- if n <= min (transcript ._resident_tail , len (transcript ._events )):
342- events = transcript ._events [- n :]
343- return events [:limit ] if limit is not None else events
333+ # suffix of the logical history (pinned/pending events survive at
334+ # their insertion positions), so a logical offset can't be mapped
335+ # into ``_events`` by suffix arithmetic — only the trailing window is
336+ # a memory read (see `Transcript._trailing_window` for the
337+ # invariant). Anything earlier must come from the provider.
338+ window = transcript ._trailing_window (count - start , limit )
339+ if window is not None :
340+ return window
344341 if transcript ._history_provider is None :
345342 raise TranscriptHistoryUnavailableError (
346343 "Full transcript history is not available from this Transcript"
@@ -698,6 +695,28 @@ def _prune_unreferenced_attachments(self) -> None:
698695 if ref not in self ._attachment_refcount :
699696 self ._attachments .pop (ref , None )
700697
698+ def _trailing_window (self , n : int , limit : int | None = None ) -> list [Event ] | None :
699+ """The newest ``n`` logical events, served from resident memory.
700+
701+ The eviction invariant this relies on — maintained by
702+ :meth:`_evict_events`, and the single place it should be reasoned
703+ about: eviction removes only the *oldest evictable* events, while
704+ pinned and pending events survive at their insertion positions. Older
705+ positions in ``_events`` may therefore be gapped relative to the
706+ logical history, but the trailing ``min(_resident_tail,
707+ len(_events))`` elements are always exactly the newest logical
708+ events, in order.
709+
710+ Returns the window (optionally capped at ``limit`` events from its
711+ start) when ``n`` lies within that guarantee, else ``None`` — the
712+ caller must then materialize from the history provider (or fail).
713+ """
714+ if n > min (self ._resident_tail , len (self ._events )):
715+ return None
716+ first = len (self ._events ) - n
717+ end = len (self ._events ) if limit is None else first + limit
718+ return self ._events [first :end ]
719+
701720 def _evict_events (self ) -> None :
702721 if not self ._bounded :
703722 return
0 commit comments