11import binascii
2- from typing import Any , Collection , Optional , TypeVar
2+ from typing import Any , Optional , Sequence
33
44from ..utils .base64 import base64 , unbase64
55from .connectiontypes import (
1111 'get_offset_with_default' , 'offset_to_cursor'
1212]
1313
14- T = TypeVar ('T' )
15-
1614
1715def 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.
2023
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.
2430 """
2531 return connection_from_array_slice (
2632 data , args ,
2733 slice_start = 0 ,
2834 array_length = len (data ),
35+ connection_type = connection_type ,
36+ edge_type = edge_type , page_info_type = page_info_type ,
2937 )
3038
3139
3240def 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.
3753
3854 This function is similar to `connection_from_array`, but is intended for use
3955 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.
4262 """
63+ args = args or {}
4364 before = args .get ('before' )
4465 after = args .get ('after' )
4566 first = args .get ('first' )
4667 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
4873 before_offset = get_offset_with_default (before , array_length )
4974 after_offset = get_offset_with_default (after , - 1 )
5075
@@ -64,12 +89,11 @@ def connection_from_array_slice(
6489
6590 # If supplied slice is too large, trim it down before mapping over it.
6691 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 )
6993 ]
7094
7195 edges = [
72- Edge (
96+ edge_type (
7397 node = value ,
7498 cursor = offset_to_cursor (start_offset + index )
7599 )
@@ -81,9 +105,9 @@ def connection_from_array_slice(
81105 lower_bound = after_offset + 1 if after else 0
82106 upper_bound = before_offset if before else array_length
83107
84- return Connection (
108+ return connection_type (
85109 edges = edges ,
86- pageInfo = PageInfo (
110+ pageInfo = page_info_type (
87111 startCursor = first_edge_cursor ,
88112 endCursor = last_edge_cursor ,
89113 hasPreviousPage = isinstance (last , int ) and start_offset > lower_bound ,
@@ -108,16 +132,27 @@ def cursor_to_offset(cursor: ConnectionCursor) -> Optional[int]:
108132 return None
109133
110134
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+ """
113142 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 )
121156 except ValueError :
122157 return None
123158 else :
0 commit comments