1
1
import binascii
2
- from typing import Any , Collection , Optional , TypeVar
2
+ from typing import Any , Optional , Sequence
3
3
4
4
from ..utils .base64 import base64 , unbase64
5
5
from .connectiontypes import (
11
11
'get_offset_with_default' , 'offset_to_cursor'
12
12
]
13
13
14
- T = TypeVar ('T' )
15
-
16
14
17
15
def connection_from_array (
18
- data : Collection , args : ConnectionArguments ) -> Connection :
19
- """Create a connection argument from a collection.
16
+ data : Sequence , args : ConnectionArguments = None ,
17
+ connection_type : Any = Connection ,
18
+ edge_type : Any = Edge , page_info_type : Any = PageInfo ) -> Connection :
19
+ """Create a connection object from a sequence of objects.
20
+
21
+ Note that different from its JavaScript counterpart which expects an array,
22
+ this function accepts any kind of sliceable object with a length.
20
23
21
- A simple function that accepts a collection (e.g. a Python list) and connection
22
- arguments, and returns a connection object for use in GraphQL. It uses array
23
- offsets as pagination, so pagination will only work if the array is static.
24
+ Given this `data` object representing the result set, and connection arguments,
25
+ this simple function returns a connection object for use in GraphQL. It uses
26
+ offsets as pagination, so pagination will only work if the data is static.
27
+
28
+ The result will use the default types provided in the `connectiontypes` module
29
+ if you don't pass custom types as arguments.
24
30
"""
25
31
return connection_from_array_slice (
26
32
data , args ,
27
33
slice_start = 0 ,
28
34
array_length = len (data ),
35
+ connection_type = connection_type ,
36
+ edge_type = edge_type , page_info_type = page_info_type ,
29
37
)
30
38
31
39
32
40
def connection_from_array_slice (
33
- array_slice : Collection , args : ConnectionArguments ,
34
- slice_start : int , array_length : int
35
- ) -> Connection :
36
- """Given a slice of a collection, returns a connection object for use in GraphQL.
41
+ array_slice : Sequence , args : ConnectionArguments = None ,
42
+ slice_start : int = 0 , array_length : int = None , array_slice_length : int = None ,
43
+ connection_type : Any = Connection ,
44
+ edge_type : Any = Edge , page_info_type : Any = PageInfo ) -> Connection :
45
+ """Create a connection object from a slice of the result set.
46
+
47
+ Note that different from its JavaScript counterpart which expects an array,
48
+ this function accepts any kind of sliceable object. This object represents
49
+ a slice of the full result set. You need to pass the start position of the
50
+ slice as `slice start` and the length of the full result set as `array_length`.
51
+ If the `array_slice` does not have a length, you need to provide it separately
52
+ in `array_slice_length` as well.
37
53
38
54
This function is similar to `connection_from_array`, but is intended for use
39
55
cases where you know the cardinality of the connection, consider it too large
40
- to materialize the entire collection, and instead wish pass in a slice of the
41
- total result large enough to cover the range specified in `args`.
56
+ to materialize the entire result set, and instead wish to pass in only a slice
57
+ of the total result large enough to cover the range specified in `args`.
58
+
59
+ If you do not provide a `slice_start`, we assume that the slice starts at
60
+ the beginning of the result set, and if you do not provide an `array_length`,
61
+ we assume that the slice ends at the end of the result set.
42
62
"""
63
+ args = args or {}
43
64
before = args .get ('before' )
44
65
after = args .get ('after' )
45
66
first = args .get ('first' )
46
67
last = args .get ('last' )
47
- slice_end = slice_start + len (array_slice )
68
+ if array_slice_length is None :
69
+ array_slice_length = len (array_slice )
70
+ slice_end = slice_start + array_slice_length
71
+ if array_length is None :
72
+ array_length = slice_end
48
73
before_offset = get_offset_with_default (before , array_length )
49
74
after_offset = get_offset_with_default (after , - 1 )
50
75
@@ -64,12 +89,11 @@ def connection_from_array_slice(
64
89
65
90
# If supplied slice is too large, trim it down before mapping over it.
66
91
trimmed_slice = array_slice [
67
- max (start_offset - slice_start , 0 ):
68
- len (array_slice ) - (slice_end - end_offset )
92
+ start_offset - slice_start :array_slice_length - (slice_end - end_offset )
69
93
]
70
94
71
95
edges = [
72
- Edge (
96
+ edge_type (
73
97
node = value ,
74
98
cursor = offset_to_cursor (start_offset + index )
75
99
)
@@ -81,9 +105,9 @@ def connection_from_array_slice(
81
105
lower_bound = after_offset + 1 if after else 0
82
106
upper_bound = before_offset if before else array_length
83
107
84
- return Connection (
108
+ return connection_type (
85
109
edges = edges ,
86
- pageInfo = PageInfo (
110
+ pageInfo = page_info_type (
87
111
startCursor = first_edge_cursor ,
88
112
endCursor = last_edge_cursor ,
89
113
hasPreviousPage = isinstance (last , int ) and start_offset > lower_bound ,
@@ -108,16 +132,27 @@ def cursor_to_offset(cursor: ConnectionCursor) -> Optional[int]:
108
132
return None
109
133
110
134
111
- def cursor_for_object_in_connection (data : Collection , obj : T ) -> Optional [Any ]:
112
- """Return the cursor associated with an object in a collection."""
135
+ def cursor_for_object_in_connection (
136
+ data : Sequence , obj : Any ) -> Optional [ConnectionCursor ]:
137
+ """Return the cursor associated with an object in a sequence.
138
+
139
+ This function uses the `index` method of the sequence if it exists,
140
+ otherwise searches the object by iterating via the `__getitem__` method.
141
+ """
113
142
try :
114
- # noinspection PyUnresolvedReferences
115
- offset = data .index (obj ) # type: ignore
116
- except AttributeError : # collection does not have an index method
117
- for offset , value in data :
118
- if value == obj :
119
- return offset
120
- return None
143
+ offset = data .index (obj )
144
+ except AttributeError :
145
+ # data does not have an index method
146
+ offset = 0
147
+ try :
148
+ while True :
149
+ if data [offset ] == obj :
150
+ break
151
+ offset += 1
152
+ except IndexError :
153
+ return None
154
+ else :
155
+ return offset_to_cursor (offset )
121
156
except ValueError :
122
157
return None
123
158
else :
0 commit comments