6
6
import inspect
7
7
from functools import partialmethod
8
8
from functools import wraps
9
+ from typing import TYPE_CHECKING
10
+ from typing import Any
11
+ from typing import Callable
9
12
10
13
from django .apps import apps as django_apps
11
14
from django .db import models
31
34
"RETURN_VALUE" ,
32
35
]
33
36
37
+ if TYPE_CHECKING :
38
+ _Model = models .Model
39
+ else :
40
+ _Model = object
41
+
34
42
35
43
class TransitionNotAllowed (Exception ):
36
44
"""Raised when a transition is not allowed"""
37
45
38
- def __init__ (self , * args , ** kwargs ):
46
+ def __init__ (self , * args , ** kwargs ) -> None :
39
47
self .object = kwargs .pop ("object" , None )
40
48
self .method = kwargs .pop ("method" , None )
41
49
super ().__init__ (* args , ** kwargs )
@@ -54,7 +62,7 @@ class ConcurrentTransition(Exception):
54
62
55
63
56
64
class Transition :
57
- def __init__ (self , method , source , target , on_error , conditions , permission , custom ):
65
+ def __init__ (self , method : Callable , source , target , on_error , conditions , permission , custom ) -> None :
58
66
self .method = method
59
67
self .source = source
60
68
self .target = target
@@ -64,10 +72,10 @@ def __init__(self, method, source, target, on_error, conditions, permission, cus
64
72
self .custom = custom
65
73
66
74
@property
67
- def name (self ):
75
+ def name (self ) -> str :
68
76
return self .method .__name__
69
77
70
- def has_perm (self , instance , user ):
78
+ def has_perm (self , instance , user ) -> bool :
71
79
if not self .permission :
72
80
return True
73
81
elif callable (self .permission ):
@@ -116,9 +124,9 @@ class FSMMeta:
116
124
Models methods transitions meta information
117
125
"""
118
126
119
- def __init__ (self , field , method ):
127
+ def __init__ (self , field , method ) -> None :
120
128
self .field = field
121
- self .transitions = {} # source -> Transition
129
+ self .transitions : dict [ str , Any ] = {} # source -> Transition
122
130
123
131
def get_transition (self , source ):
124
132
transition = self .transitions .get (source , None )
@@ -128,7 +136,7 @@ def get_transition(self, source):
128
136
transition = self .transitions .get ("+" , None )
129
137
return transition
130
138
131
- def add_transition (self , method , source , target , on_error = None , conditions = [], permission = None , custom = {}):
139
+ def add_transition (self , method , source , target , on_error = None , conditions = [], permission = None , custom = {}) -> None :
132
140
if source in self .transitions :
133
141
raise AssertionError (f"Duplicate transition for { source } state" )
134
142
@@ -142,7 +150,7 @@ def add_transition(self, method, source, target, on_error=None, conditions=[], p
142
150
custom = custom ,
143
151
)
144
152
145
- def has_transition (self , state ):
153
+ def has_transition (self , state ) -> bool :
146
154
"""
147
155
Lookup if any transition exists from current model state using current method
148
156
"""
@@ -157,7 +165,7 @@ def has_transition(self, state):
157
165
158
166
return False
159
167
160
- def conditions_met (self , instance , state ):
168
+ def conditions_met (self , instance , state ) -> bool :
161
169
"""
162
170
Check if all conditions have been met
163
171
"""
@@ -170,13 +178,13 @@ def conditions_met(self, instance, state):
170
178
else :
171
179
return all (map (lambda condition : condition (instance ), transition .conditions ))
172
180
173
- def has_transition_perm (self , instance , state , user ):
181
+ def has_transition_perm (self , instance , state , user ) -> bool :
174
182
transition = self .get_transition (state )
175
183
176
184
if not transition :
177
185
return False
178
186
else :
179
- return transition .has_perm (instance , user )
187
+ return bool ( transition .has_perm (instance , user ) )
180
188
181
189
def next_state (self , current_state ):
182
190
transition = self .get_transition (current_state )
@@ -196,15 +204,15 @@ def exception_state(self, current_state):
196
204
197
205
198
206
class FSMFieldDescriptor :
199
- def __init__ (self , field ):
207
+ def __init__ (self , field ) -> None :
200
208
self .field = field
201
209
202
210
def __get__ (self , instance , type = None ):
203
211
if instance is None :
204
212
return self
205
213
return self .field .get_state (instance )
206
214
207
- def __set__ (self , instance , value ):
215
+ def __set__ (self , instance , value ) -> None :
208
216
if self .field .protected and self .field .name in instance .__dict__ :
209
217
raise AttributeError (f"Direct { self .field .name } modification is not allowed" )
210
218
@@ -213,12 +221,12 @@ def __set__(self, instance, value):
213
221
self .field .set_state (instance , value )
214
222
215
223
216
- class FSMFieldMixin :
224
+ class FSMFieldMixin ( Field ) :
217
225
descriptor_class = FSMFieldDescriptor
218
226
219
- def __init__ (self , * args , ** kwargs ):
227
+ def __init__ (self , * args , ** kwargs ) -> None :
220
228
self .protected = kwargs .pop ("protected" , False )
221
- self .transitions = {} # cls -> (transitions name -> method)
229
+ self .transitions : dict [ Any , dict [ str , Any ]] = {} # cls -> (transitions name -> method)
222
230
self .state_proxy = {} # state -> ProxyClsRef
223
231
224
232
state_choices = kwargs .pop ("state_choices" , None )
@@ -244,7 +252,7 @@ def deconstruct(self):
244
252
def get_state (self , instance ):
245
253
# The state field may be deferred. We delegate the logic of figuring this out
246
254
# and loading the deferred field on-demand to Django's built-in DeferredAttribute class.
247
- return DeferredAttribute (self ).__get__ (instance )
255
+ return DeferredAttribute (self ).__get__ (instance ) # type: ignore[attr-defined]
248
256
249
257
def set_state (self , instance , state ):
250
258
instance .__dict__ [self .name ] = state
@@ -384,7 +392,7 @@ class FSMField(FSMFieldMixin, models.CharField):
384
392
State Machine support for Django model as CharField
385
393
"""
386
394
387
- def __init__ (self , * args , ** kwargs ):
395
+ def __init__ (self , * args , ** kwargs ) -> None :
388
396
kwargs .setdefault ("max_length" , 50 )
389
397
super ().__init__ (* args , ** kwargs )
390
398
@@ -409,7 +417,7 @@ def set_state(self, instance, state):
409
417
instance .__dict__ [self .attname ] = self .to_python (state )
410
418
411
419
412
- class ConcurrentTransitionMixin :
420
+ class ConcurrentTransitionMixin ( _Model ) :
413
421
"""
414
422
Protects a Model from undesirable effects caused by concurrently executed transitions,
415
423
e.g. running the same transition multiple times at the same time, or running different
@@ -435,7 +443,7 @@ class ConcurrentTransitionMixin:
435
443
state, thus practically negating their effect.
436
444
"""
437
445
438
- def __init__ (self , * args , ** kwargs ):
446
+ def __init__ (self , * args , ** kwargs ) -> None :
439
447
super ().__init__ (* args , ** kwargs )
440
448
self ._update_initial_state ()
441
449
@@ -453,7 +461,7 @@ def _do_update(self, base_qs, using, pk_val, values, update_fields, forced_updat
453
461
# state filter will be used to narrow down the standard filter checking only PK
454
462
state_filter = {field .attname : self .__initial_states [field .attname ] for field in filter_on }
455
463
456
- updated = super ()._do_update (
464
+ updated = super ()._do_update ( # type: ignore[misc]
457
465
base_qs = base_qs .filter (** state_filter ),
458
466
using = using ,
459
467
pk_val = pk_val ,
@@ -518,7 +526,7 @@ def _change_state(instance, *args, **kwargs):
518
526
return inner_transition
519
527
520
528
521
- def can_proceed (bound_method , check_conditions = True ):
529
+ def can_proceed (bound_method , check_conditions = True ) -> bool :
522
530
"""
523
531
Returns True if model in state allows to call bound_method
524
532
@@ -535,7 +543,7 @@ def can_proceed(bound_method, check_conditions=True):
535
543
return meta .has_transition (current_state ) and (not check_conditions or meta .conditions_met (self , current_state ))
536
544
537
545
538
- def has_transition_perm (bound_method , user ):
546
+ def has_transition_perm (bound_method , user ) -> bool :
539
547
"""
540
548
Returns True if model in state allows to call bound_method and user have rights on it
541
549
"""
@@ -546,7 +554,7 @@ def has_transition_perm(bound_method, user):
546
554
self = bound_method .__self__
547
555
current_state = meta .field .get_state (self )
548
556
549
- return (
557
+ return bool (
550
558
meta .has_transition (current_state )
551
559
and meta .conditions_met (self , current_state )
552
560
and meta .has_transition_perm (self , current_state , user )
@@ -559,7 +567,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
559
567
560
568
561
569
class RETURN_VALUE (State ):
562
- def __init__ (self , * allowed_states ):
570
+ def __init__ (self , * allowed_states ) -> None :
563
571
self .allowed_states = allowed_states if allowed_states else None
564
572
565
573
def get_state (self , model , transition , result , args = [], kwargs = {}):
@@ -570,7 +578,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
570
578
571
579
572
580
class GET_STATE (State ):
573
- def __init__ (self , func , states = None ):
581
+ def __init__ (self , func , states = None ) -> None :
574
582
self .func = func
575
583
self .allowed_states = states
576
584
0 commit comments