3636if TYPE_CHECKING :
3737 from collections .abc import Callable
3838 from collections .abc import Generator
39+ from collections .abc import Iterable
3940 from collections .abc import Sequence
4041 from typing import Any
4142
4748 CharField = models .CharField [str , str ]
4849 IntegerField = models .IntegerField [int , int ]
4950 ForeignKey = models .ForeignKey [Any , Any ]
51+
52+ _Instance = models .Model # TODO: use real type
53+ _ToDo = Any # TODO: use real type
5054else :
5155 _Model = object
5256 _Field = object
@@ -79,12 +83,12 @@ class ConcurrentTransition(Exception):
7983class Transition :
8084 def __init__ (
8185 self ,
82- method : Callable [..., Any ],
86+ method : Callable [..., str | int | None ],
8387 source : str | int | Sequence [str | int ] | State ,
84- target : str | int | State | None ,
88+ target : str | int ,
8589 on_error : str | int | None ,
86- conditions : list [Callable [[Any ], bool ]],
87- permission : str | Callable [[models . Model , UserWithPermissions ], bool ] | None ,
90+ conditions : list [Callable [[_Instance ], bool ]],
91+ permission : str | Callable [[_Instance , UserWithPermissions ], bool ] | None ,
8892 custom : dict [str , _StrOrPromise ],
8993 ) -> None :
9094 self .method = method
@@ -99,7 +103,7 @@ def __init__(
99103 def name (self ) -> str :
100104 return self .method .__name__
101105
102- def has_perm (self , instance , user : UserWithPermissions ) -> bool :
106+ def has_perm (self , instance : _Instance , user : UserWithPermissions ) -> bool :
103107 if not self .permission :
104108 return True
105109 if callable (self .permission ):
@@ -122,7 +126,7 @@ def __eq__(self, other):
122126 return False
123127
124128
125- def get_available_FIELD_transitions (instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
129+ def get_available_FIELD_transitions (instance : _Instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
126130 """
127131 List of transitions available in current model state
128132 with all conditions met
@@ -136,15 +140,15 @@ def get_available_FIELD_transitions(instance, field: FSMFieldMixin) -> Generator
136140 yield meta .get_transition (curr_state )
137141
138142
139- def get_all_FIELD_transitions (instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
143+ def get_all_FIELD_transitions (instance : _Instance , field : FSMFieldMixin ) -> Generator [Transition , None , None ]:
140144 """
141145 List of all transitions available in current model state
142146 """
143147 return field .get_all_transitions (instance .__class__ )
144148
145149
146150def get_available_user_FIELD_transitions (
147- instance , user : UserWithPermissions , field : FSMFieldMixin
151+ instance : _Instance , user : UserWithPermissions , field : FSMFieldMixin
148152) -> Generator [Transition , None , None ]:
149153 """
150154 List of transitions available in current model state
@@ -160,11 +164,11 @@ class FSMMeta:
160164 Models methods transitions meta information
161165 """
162166
163- def __init__ (self , field , method ) -> None :
167+ def __init__ (self , field : FSMFieldMixin , method : Any ) -> None :
164168 self .field = field
165- self .transitions : dict [str , Any ] = {} # source -> Transition
169+ self .transitions : dict [str , Transition ] = {} # source -> Transition
166170
167- def get_transition (self , source : str ):
171+ def get_transition (self , source : str ) -> Transition | None :
168172 transition = self .transitions .get (source , None )
169173 if transition is None :
170174 transition = self .transitions .get ("*" , None )
@@ -174,12 +178,12 @@ def get_transition(self, source: str):
174178
175179 def add_transition (
176180 self ,
177- method : Callable [..., Any ],
181+ method : Callable [..., str | int | None ],
178182 source : str ,
179183 target : str | int ,
180184 on_error : str | int | None = None ,
181- conditions : list [Callable [[Any ], bool ]] = [],
182- permission : str | Callable [[models . Model , UserWithPermissions ], bool ] | None = None ,
185+ conditions : list [Callable [[_Instance ], bool ]] = [],
186+ permission : str | Callable [[_Instance , UserWithPermissions ], bool ] | None = None ,
183187 custom : dict [str , _StrOrPromise ] = {},
184188 ) -> None :
185189 if source in self .transitions :
@@ -195,7 +199,7 @@ def add_transition(
195199 custom = custom ,
196200 )
197201
198- def has_transition (self , state ) -> bool :
202+ def has_transition (self , state : str ) -> bool :
199203 """
200204 Lookup if any transition exists from current model state using current method
201205 """
@@ -210,7 +214,7 @@ def has_transition(self, state) -> bool:
210214
211215 return False
212216
213- def conditions_met (self , instance , state ) -> bool :
217+ def conditions_met (self , instance : _Instance , state : str ) -> bool :
214218 """
215219 Check if all conditions have been met
216220 """
@@ -224,23 +228,23 @@ def conditions_met(self, instance, state) -> bool:
224228
225229 return all (condition (instance ) for condition in transition .conditions )
226230
227- def has_transition_perm (self , instance , state , user : UserWithPermissions ) -> bool :
231+ def has_transition_perm (self , instance : _Instance , state : str , user : UserWithPermissions ) -> bool :
228232 transition = self .get_transition (state )
229233
230234 if not transition :
231235 return False
232236 else :
233237 return bool (transition .has_perm (instance , user ))
234238
235- def next_state (self , current_state ) :
239+ def next_state (self , current_state : str ) -> str | int :
236240 transition = self .get_transition (current_state )
237241
238242 if transition is None :
239243 raise TransitionNotAllowed (f"No transition from { current_state } " )
240244
241245 return transition .target
242246
243- def exception_state (self , current_state ) :
247+ def exception_state (self , current_state : str ) -> str | int | None :
244248 transition = self .get_transition (current_state )
245249
246250 if transition is None :
@@ -250,15 +254,15 @@ def exception_state(self, current_state):
250254
251255
252256class FSMFieldDescriptor :
253- def __init__ (self , field ) -> None :
257+ def __init__ (self , field : FSMFieldMixin ) -> None :
254258 self .field = field
255259
256- def __get__ (self , instance , type = None ):
260+ def __get__ (self , instance : _Instance , type : Any | None = None ) -> Any :
257261 if instance is None :
258262 return self
259263 return self .field .get_state (instance )
260264
261- def __set__ (self , instance , value ) -> None :
265+ def __set__ (self , instance : _Instance , value : Any ) -> None :
262266 if self .field .protected and self .field .name in instance .__dict__ :
263267 raise AttributeError (f"Direct { self .field .name } modification is not allowed" )
264268
@@ -272,7 +276,7 @@ class FSMFieldMixin(_Field):
272276
273277 def __init__ (self , * args : Any , ** kwargs : Any ) -> None :
274278 self .protected = kwargs .pop ("protected" , False )
275- self .transitions : dict [Any , dict [str , Any ]] = {} # cls -> (transitions name -> method)
279+ self .transitions : dict [type [ _Model ] , dict [str , Any ]] = {} # cls -> (transitions name -> method)
276280 self .state_proxy = {} # state -> ProxyClsRef
277281
278282 state_choices = kwargs .pop ("state_choices" , None )
@@ -289,21 +293,21 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
289293
290294 super ().__init__ (* args , ** kwargs )
291295
292- def deconstruct (self ):
296+ def deconstruct (self ) -> Any :
293297 name , path , args , kwargs = super ().deconstruct ()
294298 if self .protected :
295299 kwargs ["protected" ] = self .protected
296300 return name , path , args , kwargs
297301
298- def get_state (self , instance ) -> Any :
302+ def get_state (self , instance : _Instance ) -> Any :
299303 # The state field may be deferred. We delegate the logic of figuring this out
300304 # and loading the deferred field on-demand to Django's built-in DeferredAttribute class.
301305 return DeferredAttribute (self ).__get__ (instance ) # type: ignore[attr-defined]
302306
303- def set_state (self , instance , state : str ) -> None :
307+ def set_state (self , instance : _Instance , state : str ) -> None :
304308 instance .__dict__ [self .name ] = state
305309
306- def set_proxy (self , instance , state : str ) -> None :
310+ def set_proxy (self , instance : _Instance , state : str ) -> None :
307311 """
308312 Change class
309313 """
@@ -324,7 +328,7 @@ def set_proxy(self, instance, state: str) -> None:
324328
325329 instance .__class__ = model
326330
327- def change_state (self , instance , method , * args : Any , ** kwargs : Any ):
331+ def change_state (self , instance : _Instance , method : _ToDo , * args : Any , ** kwargs : Any ) -> Any :
328332 meta = method ._django_fsm
329333 method_name = method .__name__
330334 current_state = self .get_state (instance )
@@ -377,7 +381,7 @@ def change_state(self, instance, method, *args: Any, **kwargs: Any):
377381
378382 return result
379383
380- def get_all_transitions (self , instance_cls ) -> Generator [Transition , None , None ]:
384+ def get_all_transitions (self , instance_cls : type [ _Model ] ) -> Generator [Transition , None , None ]:
381385 """
382386 Returns [(source, target, name, method)] for all field transitions
383387 """
@@ -389,7 +393,7 @@ def get_all_transitions(self, instance_cls) -> Generator[Transition, None, None]
389393 for transition in meta .transitions .values ():
390394 yield transition
391395
392- def contribute_to_class (self , cls , name , private_only = False , ** kwargs ) :
396+ def contribute_to_class (self , cls : type [ _Model ] , name : str , private_only : bool = False , ** kwargs : Any ) -> None :
393397 self .base_cls = cls
394398
395399 super ().contribute_to_class (cls , name , private_only = private_only , ** kwargs )
@@ -404,7 +408,7 @@ def contribute_to_class(self, cls, name, private_only=False, **kwargs):
404408
405409 class_prepared .connect (self ._collect_transitions )
406410
407- def _collect_transitions (self , * args : Any , ** kwargs : Any ):
411+ def _collect_transitions (self , * args : Any , ** kwargs : Any ) -> None :
408412 sender = kwargs ["sender" ]
409413
410414 if not issubclass (sender , self .base_cls ):
@@ -456,10 +460,10 @@ class FSMKeyField(FSMFieldMixin, ForeignKey):
456460 State Machine support for Django model
457461 """
458462
459- def get_state (self , instance ) :
463+ def get_state (self , instance : _Instance ) -> _ToDo :
460464 return instance .__dict__ [self .attname ]
461465
462- def set_state (self , instance , state ) :
466+ def set_state (self , instance : _Instance , state : str ) -> None :
463467 instance .__dict__ [self .attname ] = self .to_python (state )
464468
465469
@@ -521,7 +525,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
521525 self ._update_initial_state ()
522526
523527 @property
524- def state_fields (self ):
528+ def state_fields (self ) -> Iterable [ Any ] :
525529 return filter (lambda field : isinstance (field , FSMFieldMixin ), self ._meta .fields )
526530
527531 def _do_update (self , base_qs , using , pk_val , values , update_fields , forced_update ):
@@ -567,14 +571,14 @@ def save(self, *args: Any, **kwargs: Any) -> None:
567571
568572
569573def transition (
570- field ,
574+ field : FSMFieldMixin ,
571575 source : str | int | Sequence [str | int ] | State = "*" ,
572576 target : str | int | State | None = None ,
573577 on_error : str | int | None = None ,
574578 conditions : list [Callable [[Any ], bool ]] = [],
575579 permission : str | Callable [[models .Model , UserWithPermissions ], bool ] | None = None ,
576580 custom : dict [str , _StrOrPromise ] = {},
577- ):
581+ ) -> _ToDo :
578582 """
579583 Method decorator to mark allowed transitions.
580584
@@ -596,7 +600,7 @@ def inner_transition(func):
596600 func ._django_fsm .add_transition (func , source , target , on_error , conditions , permission , custom )
597601
598602 @wraps (func )
599- def _change_state (instance , * args : Any , ** kwargs : Any ):
603+ def _change_state (instance : _Instance , * args : Any , ** kwargs : Any ) -> _ToDo :
600604 return fsm_meta .field .change_state (instance , func , * args , ** kwargs )
601605
602606 if not wrapper_installed :
@@ -607,7 +611,7 @@ def _change_state(instance, *args: Any, **kwargs: Any):
607611 return inner_transition
608612
609613
610- def can_proceed (bound_method , check_conditions : bool = True ) -> bool :
614+ def can_proceed (bound_method : _ToDo , check_conditions : bool = True ) -> bool :
611615 """
612616 Returns True if model in state allows to call bound_method
613617
@@ -624,7 +628,7 @@ def can_proceed(bound_method, check_conditions: bool = True) -> bool:
624628 return meta .has_transition (current_state ) and (not check_conditions or meta .conditions_met (self , current_state ))
625629
626630
627- def has_transition_perm (bound_method , user : UserWithPermissions ) -> bool :
631+ def has_transition_perm (bound_method : _ToDo , user : UserWithPermissions ) -> bool :
628632 """
629633 Returns True if model in state allows to call bound_method and user have rights on it
630634 """
0 commit comments