Skip to content

Commit 07ddfab

Browse files
parfeonjguz-pubnub
andauthored
Add limit and offset configuration options (#224)
feat(here-now): add `limit` and `offset` configuration options Add `limit` (default `1000`) and `offset` parameters for `here_now` to fetch presence in portions. fix(subscribe-heartbeat): fix duplicated channels issue Fix issue because of which it was possible to add duplicated entries of `channels` and `groups` to the `subscribe`, `heartbeat`, and `leave` requests. feat(push-notifications): push type changes Add FCM push type support with GCM deprecation, and remove MPNS support due to its end of life. --------- Co-authored-by: jguz-pubnub <jakub.guz@pubnub.com>
1 parent f5be446 commit 07ddfab

37 files changed

+387
-412
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
8888
pip3 install --user --ignore-installed -r requirements-dev.txt
8989
behave --junit tests/acceptance/pam
90-
behave --junit tests/acceptance/encryption/cryptor-module.feature -t=~na=python -k
90+
behave --junit tests/acceptance/encryption/cryptor-module.feature -t=~na=python
9191
behave --junit tests/acceptance/subscribe
9292
- name: Expose acceptance tests reports
9393
uses: actions/upload-artifact@v4

.pubnub.yml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: python
2-
version: 10.4.1
2+
version: 10.5.0
33
schema: 1
44
scm: github.com/pubnub/python
55
sdks:
@@ -18,7 +18,7 @@ sdks:
1818
distributions:
1919
- distribution-type: library
2020
distribution-repository: package
21-
package-name: pubnub-10.4.1
21+
package-name: pubnub-10.5.0
2222
location: https://pypi.org/project/pubnub/
2323
supported-platforms:
2424
supported-operating-systems:
@@ -94,8 +94,8 @@ sdks:
9494
-
9595
distribution-type: library
9696
distribution-repository: git release
97-
package-name: pubnub-10.4.1
98-
location: https://github.com/pubnub/python/releases/download/10.4.1/pubnub-10.4.1.tar.gz
97+
package-name: pubnub-10.5.0
98+
location: https://github.com/pubnub/python/releases/download/10.5.0/pubnub-10.5.0.tar.gz
9999
supported-platforms:
100100
supported-operating-systems:
101101
Linux:
@@ -169,6 +169,15 @@ sdks:
169169
license-url: https://github.com/encode/httpx/blob/master/LICENSE.md
170170
is-required: Required
171171
changelog:
172+
- date: 2025-12-02
173+
version: 10.5.0
174+
changes:
175+
- type: feature
176+
text: "Add `limit` (default `1000`) and `offset` parameters for `here_now` to fetch presence in portions."
177+
- type: feature
178+
text: "Add FCM push type support with GCM deprecation, and remove MPNS support due to its end of life."
179+
- type: bug
180+
text: "Fix issue because of which it was possible to add duplicated entries of `channels` and `groups` to the `subscribe`, `heartbeat`, and `leave` requests."
172181
- date: 2025-06-05
173182
version: 10.4.1
174183
changes:

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 10.5.0
2+
December 02 2025
3+
4+
#### Added
5+
- Add `limit` (default `1000`) and `offset` parameters for `here_now` to fetch presence in portions.
6+
- Add FCM push type support with GCM deprecation, and remove MPNS support due to its end of life.
7+
8+
#### Fixed
9+
- Fix issue because of which it was possible to add duplicated entries of `channels` and `groups` to the `subscribe`, `heartbeat`, and `leave` requests.
10+
111
## 10.4.1
212
June 05 2025
313

pubnub/endpoints/presence/heartbeat.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Optional, Union, List
1+
from typing import Dict, Optional, Union, List, Set
22
from pubnub import utils
33
from pubnub.endpoints.endpoint import Endpoint
44
from pubnub.enums import HttpMethod, PNOperationType
@@ -13,22 +13,22 @@ class Heartbeat(Endpoint):
1313
def __init__(self, pubnub, channels: Union[str, List[str]] = None, channel_groups: Union[str, List[str]] = None,
1414
state: Optional[Dict[str, any]] = None):
1515
super(Heartbeat, self).__init__(pubnub)
16-
self._channels = []
17-
self._groups = []
16+
self._channels: Set[str] = set()
17+
self._groups: Set[str] = set()
1818
if channels:
19-
utils.extend_list(self._channels, channels)
19+
utils.update_set(self._channels, channels)
2020

2121
if channel_groups:
22-
utils.extend_list(self._groups, channel_groups)
22+
utils.update_set(self._groups, channel_groups)
2323

2424
self._state = state
2525

2626
def channels(self, channels: Union[str, List[str]]) -> 'Heartbeat':
27-
utils.extend_list(self._channels, channels)
27+
utils.update_set(self._channels, channels)
2828
return self
2929

3030
def channel_groups(self, channel_groups: Union[str, List[str]]) -> 'Heartbeat':
31-
utils.extend_list(self._groups, channel_groups)
31+
utils.update_set(self._groups, channel_groups)
3232
return self
3333

3434
def state(self, state: Dict[str, any]) -> 'Heartbeat':
@@ -46,14 +46,14 @@ def validate_params(self):
4646
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
4747

4848
def build_path(self):
49-
channels = utils.join_channels(self._channels)
49+
channels = utils.join_channels(self._channels, True)
5050
return Heartbeat.HEARTBEAT_PATH % (self.pubnub.config.subscribe_key, channels)
5151

5252
def custom_params(self):
5353
params = {'heartbeat': str(self.pubnub.config.presence_timeout)}
5454

5555
if len(self._groups) > 0:
56-
params['channel-group'] = utils.join_items(self._groups)
56+
params['channel-group'] = utils.join_items(self._groups, True)
5757

5858
if self._state is not None and len(self._state) > 0:
5959
params['state'] = utils.url_write(self._state)

pubnub/endpoints/presence/here_now.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def __init__(self, pubnub, channels: Union[str, List[str]] = None, channel_group
2929

3030
self._include_state = include_state
3131
self._include_uuids = include_uuids
32+
self._offset = None
33+
self._limit = 1000
3234

3335
def channels(self, channels: Union[str, List[str]]) -> 'HereNow':
3436
utils.extend_list(self._channels, channels)
@@ -46,8 +48,16 @@ def include_uuids(self, include_uuids) -> 'HereNow':
4648
self._include_uuids = include_uuids
4749
return self
4850

51+
def limit(self, limit: int) -> 'HereNow':
52+
self._limit = limit
53+
return self
54+
55+
def offset(self, offset: int) -> 'HereNow':
56+
self._offset = offset
57+
return self
58+
4959
def custom_params(self):
50-
params = {}
60+
params = {'limit': self._limit}
5161

5262
if len(self._channel_groups) > 0:
5363
params['channel-group'] = utils.join_items_and_encode(self._channel_groups)
@@ -58,6 +68,9 @@ def custom_params(self):
5868
if not self._include_uuids:
5969
params['disable_uuids'] = "1"
6070

71+
if self._offset is not None:
72+
params['offset'] = self._offset
73+
6174
return params
6275

6376
def build_path(self):

pubnub/endpoints/presence/leave.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Set, Union, List
2+
13
from pubnub import utils
24
from pubnub.endpoints.endpoint import Endpoint
35
from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING
@@ -11,38 +13,30 @@ class Leave(Endpoint):
1113

1214
def __init__(self, pubnub):
1315
Endpoint.__init__(self, pubnub)
14-
self._channels = []
15-
self._groups = []
16-
17-
def channels(self, channels):
18-
if isinstance(channels, (list, tuple)):
19-
self._channels.extend(channels)
20-
else:
21-
self._channels.extend(utils.split_items(channels))
16+
self._channels: Set[str] = set()
17+
self._groups: Set[str] = set()
2218

19+
def channels(self, channels: Union[str, List[str]]) -> 'Leave':
20+
utils.update_set(self._channels, channels)
2321
return self
2422

25-
def channel_groups(self, channel_groups):
26-
if isinstance(channel_groups, (list, tuple)):
27-
self._groups.extend(channel_groups)
28-
else:
29-
self._groups.extend(utils.split_items(channel_groups))
30-
23+
def channel_groups(self, channel_groups: Union[str, List[str]]) -> 'Leave':
24+
utils.update_set(self._groups, channel_groups)
3125
return self
3226

3327
def custom_params(self):
3428
params = {}
3529

3630
if len(self._groups) > 0:
37-
params['channel-group'] = utils.join_items(self._groups)
31+
params['channel-group'] = utils.join_items(self._groups, True)
3832

3933
if hasattr(self.pubnub, '_subscription_manager'):
4034
params.update(self.pubnub._subscription_manager.get_custom_params())
4135

4236
return params
4337

4438
def build_path(self):
45-
return Leave.LEAVE_PATH % (self.pubnub.config.subscribe_key, utils.join_channels(self._channels))
39+
return Leave.LEAVE_PATH % (self.pubnub.config.subscribe_key, utils.join_channels(self._channels, True))
4640

4741
def http_method(self):
4842
return HttpMethod.GET
@@ -60,10 +54,10 @@ def is_auth_required(self):
6054
return True
6155

6256
def affected_channels(self):
63-
return self._channels
57+
return sorted(self._channels)
6458

6559
def affected_channels_groups(self):
66-
return self._groups
60+
return sorted(self._groups)
6761

6862
def request_timeout(self):
6963
return self.pubnub.config.non_subscribe_request_timeout

pubnub/endpoints/pubsub/subscribe.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Union, List
1+
from typing import Optional, Union, List, Set
22
from pubnub import utils
33
from pubnub.endpoints.endpoint import Endpoint
44
from pubnub.enums import HttpMethod, PNOperationType
@@ -25,12 +25,12 @@ def __init__(self, pubnub, channels: Union[str, List[str]] = None,
2525
with_presence: Optional[str] = None, state: Optional[str] = None):
2626

2727
super(Subscribe, self).__init__(pubnub)
28-
self._channels = []
28+
self._channels: Set[str] = set()
29+
self._groups: Set[str] = set()
2930
if channels:
30-
utils.extend_list(self._channels, channels)
31-
self._groups = []
31+
utils.update_set(self._channels, channels)
3232
if groups:
33-
utils.extend_list(self._groups, groups)
33+
utils.update_set(self._groups, groups)
3434

3535
self._region = region
3636
self._filter_expression = filter_expression
@@ -39,11 +39,11 @@ def __init__(self, pubnub, channels: Union[str, List[str]] = None,
3939
self._state = state
4040

4141
def channels(self, channels: Union[str, List[str]]) -> 'Subscribe':
42-
utils.extend_list(self._channels, channels)
42+
utils.update_set(self._channels, channels)
4343
return self
4444

4545
def channel_groups(self, groups: Union[str, List[str]]) -> 'Subscribe':
46-
utils.extend_list(self._groups, groups)
46+
utils.update_set(self._groups, groups)
4747
return self
4848

4949
def timetoken(self, timetoken) -> 'Subscribe':
@@ -72,14 +72,14 @@ def validate_params(self):
7272
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
7373

7474
def build_path(self):
75-
channels = utils.join_channels(self._channels)
75+
channels = utils.join_channels(self._channels, True)
7676
return Subscribe.SUBSCRIBE_PATH % (self.pubnub.config.subscribe_key, channels)
7777

7878
def custom_params(self):
7979
params = {}
8080

8181
if len(self._groups) > 0:
82-
params['channel-group'] = utils.join_items_and_encode(self._groups)
82+
params['channel-group'] = utils.join_items_and_encode(self._groups, True)
8383

8484
if self._filter_expression is not None and len(self._filter_expression) > 0:
8585
params['filter-expr'] = utils.url_encode(self._filter_expression)
@@ -108,10 +108,10 @@ def is_auth_required(self):
108108
return True
109109

110110
def affected_channels(self):
111-
return self._channels
111+
return sorted(self._channels)
112112

113113
def affected_channels_groups(self):
114-
return self._groups
114+
return sorted(self._groups)
115115

116116
def request_timeout(self):
117117
return self.pubnub.config.subscribe_request_timeout

pubnub/enums.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ class PNReconnectionPolicy(object):
143143

144144
class PNPushType(object):
145145
APNS = 1
146-
MPNS = 2
147-
GCM = 3
146+
GCM = 3 # Deprecated: Use FCM instead. GCM has been replaced by FCM (Firebase Cloud Messaging)
148147
APNS2 = 4
148+
FCM = 5
149149

150150

151151
class PNResourceType(object):

pubnub/event_engine/effects.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async def handshake_async(self, channels, groups, stop_event, timetoken: int = 0
8888
self.logger.warning(f'Handshake failed: {response.status.error_data.__dict__}')
8989
handshake_failure = events.HandshakeFailureEvent(response.status.error_data, 1, timetoken=timetoken)
9090
self.event_engine.trigger(handshake_failure)
91-
else:
91+
elif 't' in response.result:
9292
cursor = response.result['t']
9393
timetoken = timetoken if timetoken > 0 else cursor['t']
9494
region = cursor['r']
@@ -134,7 +134,7 @@ async def receive_messages_async(self, channels, groups, timetoken, region):
134134
self.logger.warning(f'Recieve messages failed: {response.status.error_data.__dict__}')
135135
recieve_failure = events.ReceiveFailureEvent(response.status.error_data, 1, timetoken=timetoken)
136136
self.event_engine.trigger(recieve_failure)
137-
else:
137+
elif 't' in response.result:
138138
cursor = response.result['t']
139139
timetoken = cursor['t']
140140
region = cursor['r']

pubnub/event_engine/models/states.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ def reconnect_failure(self, event: events.ReceiveReconnectFailureEvent, context:
568568
return PNTransition(
569569
state=ReceiveReconnectingState,
570570
context=self._context,
571-
invocation=invocations.EmitStatusInvocation(PNStatusCategory.UnexpectedDisconnectCategory,
571+
invocation=invocations.EmitStatusInvocation(PNStatusCategory.PNUnexpectedDisconnectCategory,
572572
operation=PNOperationType.PNSubscribeOperation,
573573
context=self._context)
574574
)

0 commit comments

Comments
 (0)