6
6
from collections import Sequence
7
7
except ImportError :
8
8
from collections .abc import Sequence
9
+ try :
10
+ # enum in stdlib as of py3.4
11
+ from enum import IntEnum # pylint: disable=import-error
12
+ except ImportError :
13
+ # vendored backport module
14
+ from kafka .vendor .enum34 import IntEnum
9
15
import logging
10
16
import random
11
17
import re
20
26
log = logging .getLogger (__name__ )
21
27
22
28
29
+ class SubscriptionType (IntEnum ):
30
+ NONE = 0
31
+ AUTO_TOPICS = 1
32
+ AUTO_PATTERN = 2
33
+ USER_ASSIGNED = 3
34
+
35
+
23
36
class SubscriptionState (object ):
24
37
"""
25
38
A class for tracking the topics, partitions, and offsets for the consumer.
@@ -67,6 +80,7 @@ def __init__(self, offset_reset_strategy='earliest'):
67
80
self ._default_offset_reset_strategy = offset_reset_strategy
68
81
69
82
self .subscription = None # set() or None
83
+ self .subscription_type = SubscriptionType .NONE
70
84
self .subscribed_pattern = None # regex str or None
71
85
self ._group_subscription = set ()
72
86
self ._user_assignment = set ()
@@ -76,6 +90,14 @@ def __init__(self, offset_reset_strategy='earliest'):
76
90
# initialize to true for the consumers to fetch offset upon starting up
77
91
self .needs_fetch_committed_offsets = True
78
92
93
+ def _set_subscription_type (self , subscription_type ):
94
+ if not isinstance (subscription_type , SubscriptionType ):
95
+ raise ValueError ('SubscriptionType enum required' )
96
+ if self .subscription_type == SubscriptionType .NONE :
97
+ self .subscription_type = subscription_type
98
+ elif self .subscription_type != subscription_type :
99
+ raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
100
+
79
101
def subscribe (self , topics = (), pattern = None , listener = None ):
80
102
"""Subscribe to a list of topics, or a topic regex pattern.
81
103
@@ -111,17 +133,19 @@ def subscribe(self, topics=(), pattern=None, listener=None):
111
133
guaranteed, however, that the partitions revoked/assigned
112
134
through this interface are from topics subscribed in this call.
113
135
"""
114
- if self ._user_assignment or (topics and pattern ):
115
- raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
116
136
assert topics or pattern , 'Must provide topics or pattern'
137
+ if (topics and pattern ):
138
+ raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
117
139
118
- if pattern :
140
+ elif pattern :
141
+ self ._set_subscription_type (SubscriptionType .AUTO_PATTERN )
119
142
log .info ('Subscribing to pattern: /%s/' , pattern )
120
143
self .subscription = set ()
121
144
self .subscribed_pattern = re .compile (pattern )
122
145
else :
123
146
if isinstance (topics , str ) or not isinstance (topics , Sequence ):
124
147
raise TypeError ('Topics must be a list (or non-str sequence)' )
148
+ self ._set_subscription_type (SubscriptionType .AUTO_TOPICS )
125
149
self .change_subscription (topics )
126
150
127
151
if listener and not isinstance (listener , ConsumerRebalanceListener ):
@@ -141,7 +165,7 @@ def change_subscription(self, topics):
141
165
- a topic name is '.' or '..' or
142
166
- a topic name does not consist of ASCII-characters/'-'/'_'/'.'
143
167
"""
144
- if self ._user_assignment :
168
+ if not self .partitions_auto_assigned () :
145
169
raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
146
170
147
171
if isinstance (topics , six .string_types ):
@@ -168,13 +192,13 @@ def group_subscribe(self, topics):
168
192
Arguments:
169
193
topics (list of str): topics to add to the group subscription
170
194
"""
171
- if self ._user_assignment :
195
+ if not self .partitions_auto_assigned () :
172
196
raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
173
197
self ._group_subscription .update (topics )
174
198
175
199
def reset_group_subscription (self ):
176
200
"""Reset the group's subscription to only contain topics subscribed by this consumer."""
177
- if self ._user_assignment :
201
+ if not self .partitions_auto_assigned () :
178
202
raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
179
203
assert self .subscription is not None , 'Subscription required'
180
204
self ._group_subscription .intersection_update (self .subscription )
@@ -197,9 +221,7 @@ def assign_from_user(self, partitions):
197
221
Raises:
198
222
IllegalStateError: if consumer has already called subscribe()
199
223
"""
200
- if self .subscription is not None :
201
- raise IllegalStateError (self ._SUBSCRIPTION_EXCEPTION_MESSAGE )
202
-
224
+ self ._set_subscription_type (SubscriptionType .USER_ASSIGNED )
203
225
if self ._user_assignment != set (partitions ):
204
226
self ._user_assignment = set (partitions )
205
227
self ._set_assignment ({partition : self .assignment .get (partition , TopicPartitionState ())
@@ -250,6 +272,7 @@ def unsubscribe(self):
250
272
self ._user_assignment .clear ()
251
273
self .assignment .clear ()
252
274
self .subscribed_pattern = None
275
+ self .subscription_type = SubscriptionType .NONE
253
276
254
277
def group_subscription (self ):
255
278
"""Get the topic subscription for the group.
@@ -300,7 +323,7 @@ def fetchable_partitions(self):
300
323
301
324
def partitions_auto_assigned (self ):
302
325
"""Return True unless user supplied partitions manually."""
303
- return self .subscription is not None
326
+ return self .subscription_type in ( SubscriptionType . AUTO_TOPICS , SubscriptionType . AUTO_PATTERN )
304
327
305
328
def all_consumed_offsets (self ):
306
329
"""Returns consumed offsets as {TopicPartition: OffsetAndMetadata}"""
0 commit comments