Coverage for src / lexigram / contracts / domain / pagination.py: 3%

29 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-19 05:41 +0800

1"""Pagination data models for Lexigram Framework.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass 

6from typing import Any, Generic, Protocol, TypeVar, runtime_checkable 

7 

8T = TypeVar("T") 

9 

10 

11@runtime_checkable 

12class OffsetPageProtocol(Protocol): 

13 """Protocol for offset-based page request parameters. 

14 

15 Implemented by offset/limit style pagination inputs. Use this for 

16 code that should work with any offset-pagination type. 

17 """ 

18 

19 @property 

20 def offset(self) -> int: 

21 """Zero-based record offset.""" 

22 ... 

23 

24 @property 

25 def limit(self) -> int: 

26 """Maximum number of records to return.""" 

27 ... 

28 

29 

30@runtime_checkable 

31class CursorPageProtocol(Protocol, Generic[T]): 

32 """Protocol for cursor-based paginated result pages. 

33 

34 Implemented by ``CursorPage`` and Relay-compliant GraphQL page types. 

35 Use this for code that should work with any cursor-pagination result. 

36 """ 

37 

38 @property 

39 def items(self) -> list[T]: 

40 """Items in this page.""" 

41 ... 

42 

43 @property 

44 def next_cursor(self) -> str | None: 

45 """Opaque cursor for the next page, or ``None`` if no more.""" 

46 ... 

47 

48 @property 

49 def has_more(self) -> bool: 

50 """Whether there are more results after this page.""" 

51 ... 

52 

53 

54@dataclass(frozen=True) 

55class CursorPage(Generic[T]): 

56 """Cursor-based pagination for large datasets. 

57 

58 Attributes: 

59 items: The entities in this page. 

60 next_cursor: Opaque cursor for the next page, or None if no more. 

61 prev_cursor: Opaque cursor for the previous page, or None. 

62 has_more: Whether there are more results after this page. 

63 has_previous: Whether there are results before this page. 

64 total_count: Optional total count (may be None for performance). 

65 """ 

66 

67 items: list[T] 

68 next_cursor: str | None = None 

69 prev_cursor: str | None = None 

70 has_more: bool = False 

71 has_previous: bool = False 

72 total_count: int | None = None 

73 

74 def to_dict(self) -> dict[str, Any]: 

75 """Serialize to API-friendly dictionary.""" 

76 return { 

77 "items": self.items, 

78 "next_cursor": self.next_cursor, 

79 "prev_cursor": self.prev_cursor, 

80 "has_more": self.has_more, 

81 "has_previous": self.has_previous, 

82 "total_count": self.total_count, 

83 } 

84 

85 

86__all__ = [ 

87 "CursorPage", 

88 "CursorPageProtocol", 

89 "OffsetPageProtocol", 

90 "T", 

91]