3535if TYPE_CHECKING :
3636 from collections .abc import Callable
3737 from collections .abc import Generator
38+ from collections .abc import Iterable
3839 from collections .abc import Sequence
3940 from typing import Any
4041
4647 CharField = models .CharField [str , str ]
4748 IntegerField = models .IntegerField [int , int ]
4849 ForeignKey = models .ForeignKey [Any , Any ]
50+
51+ _Instance = models .Model # TODO: use real type
52+ _ToDo = Any # TODO: use real type
4953else :
5054 _Model = object
5155 _Field = object
@@ -78,12 +82,12 @@ class ConcurrentTransition(Exception):
7882class Transition :
7983 def __init__ (
8084 self ,
81- method : Callable [..., Any ],
85+ method : Callable [..., str | int | None ],
8286 source : str | int | Sequence [str | int ] | State ,
83- target : str | int | State | None ,
87+ target : str | int ,
8488 on_error : str | int | None ,
85- conditions : list [Callable [[Any ], bool ]],
86- permission : str | Callable [[models . Model , UserWithPermissions ], bool ] | None ,
89+ conditions : list [Callable [[_Instance ], bool ]],
90+ permission : str | Callable [[_Instance , UserWithPermissions ], bool ] | None ,
8791 custom : dict [str , _StrOrPromise ],
8892 ) -> None :
8993 self .method = method
@@ -98,7 +102,7 @@ def __init__(
98102 def name (self ) -> str :
99103 return self .method .__name__
100104
101- def has_perm (self , instance , user : UserWithPermissions ) -> bool :
105+ def has_perm (self , instance : _Instance , user : UserWithPermissions ) -> bool :
102106 if not self .permission :
103107 return True
104108 elif callable (self .permission ):
@@ -111,7 +115,7 @@ def has_perm(self, instance, user: UserWithPermissions) -> bool:
111115 return False
112116
113117
114- def get_available_FIELD_transitions (instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
118+ def get_available_FIELD_transitions (instance : _Instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
115119 """
116120 List of transitions available in current model state
117121 with all conditions met
@@ -125,15 +129,15 @@ def get_available_FIELD_transitions(instance, field: FSMFieldMixin) -> Generator
125129 yield meta .get_transition (curr_state )
126130
127131
128- def get_all_FIELD_transitions (instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
132+ def get_all_FIELD_transitions (instance : _Instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
129133 """
130134 List of all transitions available in current model state
131135 """
132136 return field .get_all_transitions (instance .__class__ )
133137
134138
135139def get_available_user_FIELD_transitions (
136- instance , user : UserWithPermissions , field : FSMFieldMixin
140+ instance : _Instance , user : UserWithPermissions , field : FSMFieldMixin
137141) -> Generator [Transition , None , None ]:
138142 """
139143 List of transitions available in current model state
@@ -149,11 +153,11 @@ class FSMMeta:
149153 Models methods transitions meta information
150154 """
151155
152- def __init__ (self , field , method ) -> None :
156+ def __init__ (self , field : FSMFieldMixin , method : Any ) -> None :
153157 self .field = field
154- self .transitions : dict [str , Any ] = {} # source -> Transition
158+ self .transitions : dict [str , Transition ] = {} # source -> Transition
155159
156- def get_transition (self , source : str ):
160+ def get_transition (self , source : str ) -> Transition | None :
157161 transition = self .transitions .get (source , None )
158162 if transition is None :
159163 transition = self .transitions .get ("*" , None )
@@ -163,12 +167,12 @@ def get_transition(self, source: str):
163167
164168 def add_transition (
165169 self ,
166- method : Callable [..., Any ],
170+ method : Callable [..., str | int | None ],
167171 source : str ,
168172 target : str | int ,
169173 on_error : str | int | None = None ,
170- conditions : list [Callable [[Any ], bool ]] = [],
171- permission : str | Callable [[models . Model , UserWithPermissions ], bool ] | None = None ,
174+ conditions : list [Callable [[_Instance ], bool ]] = [],
175+ permission : str | Callable [[_Instance , UserWithPermissions ], bool ] | None = None ,
172176 custom : dict [str , _StrOrPromise ] = {},
173177 ) -> None :
174178 if source in self .transitions :
@@ -184,7 +188,7 @@ def add_transition(
184188 custom = custom ,
185189 )
186190
187- def has_transition (self , state ) -> bool :
191+ def has_transition (self , state : str ) -> bool :
188192 """
189193 Lookup if any transition exists from current model state using current method
190194 """
@@ -199,7 +203,7 @@ def has_transition(self, state) -> bool:
199203
200204 return False
201205
202- def conditions_met (self , instance , state ) -> bool :
206+ def conditions_met (self , instance : _Instance , state : str ) -> bool :
203207 """
204208 Check if all conditions have been met
205209 """
@@ -212,23 +216,23 @@ def conditions_met(self, instance, state) -> bool:
212216 else :
213217 return all (map (lambda condition : condition (instance ), transition .conditions ))
214218
215- def has_transition_perm (self , instance , state , user : UserWithPermissions ) -> bool :
219+ def has_transition_perm (self , instance : _Instance , state : str , user : UserWithPermissions ) -> bool :
216220 transition = self .get_transition (state )
217221
218222 if not transition :
219223 return False
220224 else :
221225 return bool (transition .has_perm (instance , user ))
222226
223- def next_state (self , current_state ) :
227+ def next_state (self , current_state : str ) -> str | int :
224228 transition = self .get_transition (current_state )
225229
226230 if transition is None :
227231 raise TransitionNotAllowed (f"No transition from { current_state } " )
228232
229233 return transition .target
230234
231- def exception_state (self , current_state ) :
235+ def exception_state (self , current_state : str ) -> str | int | None :
232236 transition = self .get_transition (current_state )
233237
234238 if transition is None :
@@ -238,15 +242,15 @@ def exception_state(self, current_state):
238242
239243
240244class FSMFieldDescriptor :
241- def __init__ (self , field ) -> None :
245+ def __init__ (self , field : FSMFieldMixin ) -> None :
242246 self .field = field
243247
244- def __get__ (self , instance , type = None ):
248+ def __get__ (self , instance : _Instance , type : Any | None = None ) -> Any :
245249 if instance is None :
246250 return self
247251 return self .field .get_state (instance )
248252
249- def __set__ (self , instance , value ) -> None :
253+ def __set__ (self , instance : _Instance , value : Any ) -> None :
250254 if self .field .protected and self .field .name in instance .__dict__ :
251255 raise AttributeError (f"Direct { self .field .name } modification is not allowed" )
252256
@@ -260,7 +264,7 @@ class FSMFieldMixin(_Field):
260264
261265 def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
262266 self .protected = kwargs .pop ("protected" , False )
263- self .transitions : dict [Any , dict [str , Any ]] = {} # cls -> (transitions name -> method)
267+ self .transitions : dict [type [ _Model ] , dict [str , Any ]] = {} # cls -> (transitions name -> method)
264268 self .state_proxy = {} # state -> ProxyClsRef
265269
266270 state_choices = kwargs .pop ("state_choices" , None )
@@ -277,21 +281,21 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
277281
278282 super ().__init__ (* args , ** kwargs )
279283
280- def deconstruct (self ):
284+ def deconstruct (self ) -> Any :
281285 name , path , args , kwargs = super ().deconstruct ()
282286 if self .protected :
283287 kwargs ["protected" ] = self .protected
284288 return name , path , args , kwargs
285289
286- def get_state (self , instance ) -> Any :
290+ def get_state (self , instance : _Instance ) -> Any :
287291 # The state field may be deferred. We delegate the logic of figuring this out
288292 # and loading the deferred field on-demand to Django's built-in DeferredAttribute class.
289293 return DeferredAttribute (self ).__get__ (instance ) # type: ignore[attr-defined]
290294
291- def set_state (self , instance , state : str ) -> None :
295+ def set_state (self , instance : _Instance , state : str ) -> None :
292296 instance .__dict__ [self .name ] = state
293297
294- def set_proxy (self , instance , state : str ) -> None :
298+ def set_proxy (self , instance : _Instance , state : str ) -> None :
295299 """
296300 Change class
297301 """
@@ -312,7 +316,7 @@ def set_proxy(self, instance, state: str) -> None:
312316
313317 instance .__class__ = model
314318
315- def change_state (self , instance , method , * args : Any , ** kwargs : Any ):
319+ def change_state (self , instance : _Instance , method : _ToDo , * args : Any , ** kwargs : Any ) -> Any :
316320 meta = method ._django_fsm
317321 method_name = method .__name__
318322 current_state = self .get_state (instance )
@@ -365,7 +369,7 @@ def change_state(self, instance, method, *args: Any, **kwargs: Any):
365369
366370 return result
367371
368- def get_all_transitions (self , instance_cls ) -> Generator [Transition , None , None ]:
372+ def get_all_transitions (self , instance_cls : type [ _Model ] ) -> Generator [Transition , None , None ]:
369373 """
370374 Returns [(source, target, name, method)] for all field transitions
371375 """
@@ -377,7 +381,7 @@ def get_all_transitions(self, instance_cls) -> Generator[Transition, None, None]
377381 for transition in meta .transitions .values ():
378382 yield transition
379383
380- def contribute_to_class (self , cls , name , private_only = False , ** kwargs ) :
384+ def contribute_to_class (self , cls : type [ _Model ] , name : str , private_only : bool = False , ** kwargs : Any ) -> None :
381385 self .base_cls = cls
382386
383387 super ().contribute_to_class (cls , name , private_only = private_only , ** kwargs )
@@ -392,7 +396,7 @@ def contribute_to_class(self, cls, name, private_only=False, **kwargs):
392396
393397 class_prepared .connect (self ._collect_transitions )
394398
395- def _collect_transitions (self , * args : Any , ** kwargs : Any ):
399+ def _collect_transitions (self , * args : Any , ** kwargs : Any ) -> None :
396400 sender = kwargs ["sender" ]
397401
398402 if not issubclass (sender , self .base_cls ):
@@ -444,10 +448,10 @@ class FSMKeyField(FSMFieldMixin, ForeignKey):
444448 State Machine support for Django model
445449 """
446450
447- def get_state (self , instance ) :
451+ def get_state (self , instance : _Instance ) -> _ToDo :
448452 return instance .__dict__ [self .attname ]
449453
450- def set_state (self , instance , state ) :
454+ def set_state (self , instance : _Instance , state : str ) -> None :
451455 instance .__dict__ [self .attname ] = self .to_python (state )
452456
453457
@@ -482,7 +486,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
482486 self ._update_initial_state ()
483487
484488 @property
485- def state_fields (self ):
489+ def state_fields (self ) -> Iterable [ Any ] :
486490 return filter (lambda field : isinstance (field , FSMFieldMixin ), self ._meta .fields )
487491
488492 def _do_update (self , base_qs , using , pk_val , values , update_fields , forced_update ):
@@ -528,14 +532,14 @@ def save(self, *args: Any, **kwargs: Any) -> None:
528532
529533
530534def transition (
531- field ,
535+ field : FSMFieldMixin ,
532536 source : str | int | Sequence [str | int ] | State = "*" ,
533537 target : str | int | State | None = None ,
534538 on_error : str | int | None = None ,
535539 conditions : list [Callable [[Any ], bool ]] = [],
536540 permission : str | Callable [[models .Model , UserWithPermissions ], bool ] | None = None ,
537541 custom : dict [str , _StrOrPromise ] = {},
538- ):
542+ ) -> _ToDo :
539543 """
540544 Method decorator to mark allowed transitions.
541545
@@ -557,7 +561,7 @@ def inner_transition(func):
557561 func ._django_fsm .add_transition (func , source , target , on_error , conditions , permission , custom )
558562
559563 @wraps (func )
560- def _change_state (instance , * args : Any , ** kwargs : Any ):
564+ def _change_state (instance : _Instance , * args : Any , ** kwargs : Any ) -> _ToDo :
561565 return fsm_meta .field .change_state (instance , func , * args , ** kwargs )
562566
563567 if not wrapper_installed :
@@ -568,7 +572,7 @@ def _change_state(instance, *args: Any, **kwargs: Any):
568572 return inner_transition
569573
570574
571- def can_proceed (bound_method , check_conditions : bool = True ) -> bool :
575+ def can_proceed (bound_method : _ToDo , check_conditions : bool = True ) -> bool :
572576 """
573577 Returns True if model in state allows to call bound_method
574578
@@ -585,7 +589,7 @@ def can_proceed(bound_method, check_conditions: bool = True) -> bool:
585589 return meta .has_transition (current_state ) and (not check_conditions or meta .conditions_met (self , current_state ))
586590
587591
588- def has_transition_perm (bound_method , user : UserWithPermissions ) -> bool :
592+ def has_transition_perm (bound_method : _ToDo , user : UserWithPermissions ) -> bool :
589593 """
590594 Returns True if model in state allows to call bound_method and user have rights on it
591595 """
0 commit comments