Skip to content

Commit 12c4184

Browse files
committed
step 1
1 parent e2cc04d commit 12c4184

File tree

2 files changed

+35
-27
lines changed

2 files changed

+35
-27
lines changed

.mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ strict_equality = True
99
extra_checks = True
1010

1111
# Strongly recommend enabling this one as soon as you can
12-
; check_untyped_defs = True
12+
check_untyped_defs = True
1313

1414
# These shouldn't be too much additional work, but may be tricky to
1515
# get passing if you use a lot of untyped libraries

django_fsm/__init__.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import inspect
77
from functools import partialmethod
88
from functools import wraps
9+
from typing import TYPE_CHECKING
10+
from typing import Any
11+
from typing import Callable
912

1013
from django.apps import apps as django_apps
1114
from django.db import models
@@ -31,11 +34,16 @@
3134
"RETURN_VALUE",
3235
]
3336

37+
if TYPE_CHECKING:
38+
_Model = models.Model
39+
else:
40+
_Model = object
41+
3442

3543
class TransitionNotAllowed(Exception):
3644
"""Raised when a transition is not allowed"""
3745

38-
def __init__(self, *args, **kwargs):
46+
def __init__(self, *args, **kwargs) -> None:
3947
self.object = kwargs.pop("object", None)
4048
self.method = kwargs.pop("method", None)
4149
super().__init__(*args, **kwargs)
@@ -54,7 +62,7 @@ class ConcurrentTransition(Exception):
5462

5563

5664
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:
5866
self.method = method
5967
self.source = source
6068
self.target = target
@@ -64,10 +72,10 @@ def __init__(self, method, source, target, on_error, conditions, permission, cus
6472
self.custom = custom
6573

6674
@property
67-
def name(self):
75+
def name(self) -> str:
6876
return self.method.__name__
6977

70-
def has_perm(self, instance, user):
78+
def has_perm(self, instance, user) -> bool:
7179
if not self.permission:
7280
return True
7381
elif callable(self.permission):
@@ -116,9 +124,9 @@ class FSMMeta:
116124
Models methods transitions meta information
117125
"""
118126

119-
def __init__(self, field, method):
127+
def __init__(self, field, method) -> None:
120128
self.field = field
121-
self.transitions = {} # source -> Transition
129+
self.transitions: dict[str, Any] = {} # source -> Transition
122130

123131
def get_transition(self, source):
124132
transition = self.transitions.get(source, None)
@@ -128,7 +136,7 @@ def get_transition(self, source):
128136
transition = self.transitions.get("+", None)
129137
return transition
130138

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:
132140
if source in self.transitions:
133141
raise AssertionError(f"Duplicate transition for {source} state")
134142

@@ -142,7 +150,7 @@ def add_transition(self, method, source, target, on_error=None, conditions=[], p
142150
custom=custom,
143151
)
144152

145-
def has_transition(self, state):
153+
def has_transition(self, state) -> bool:
146154
"""
147155
Lookup if any transition exists from current model state using current method
148156
"""
@@ -157,7 +165,7 @@ def has_transition(self, state):
157165

158166
return False
159167

160-
def conditions_met(self, instance, state):
168+
def conditions_met(self, instance, state) -> bool:
161169
"""
162170
Check if all conditions have been met
163171
"""
@@ -170,13 +178,13 @@ def conditions_met(self, instance, state):
170178
else:
171179
return all(map(lambda condition: condition(instance), transition.conditions))
172180

173-
def has_transition_perm(self, instance, state, user):
181+
def has_transition_perm(self, instance, state, user) -> bool:
174182
transition = self.get_transition(state)
175183

176184
if not transition:
177185
return False
178186
else:
179-
return transition.has_perm(instance, user)
187+
return bool(transition.has_perm(instance, user))
180188

181189
def next_state(self, current_state):
182190
transition = self.get_transition(current_state)
@@ -196,15 +204,15 @@ def exception_state(self, current_state):
196204

197205

198206
class FSMFieldDescriptor:
199-
def __init__(self, field):
207+
def __init__(self, field) -> None:
200208
self.field = field
201209

202210
def __get__(self, instance, type=None):
203211
if instance is None:
204212
return self
205213
return self.field.get_state(instance)
206214

207-
def __set__(self, instance, value):
215+
def __set__(self, instance, value) -> None:
208216
if self.field.protected and self.field.name in instance.__dict__:
209217
raise AttributeError(f"Direct {self.field.name} modification is not allowed")
210218

@@ -213,12 +221,12 @@ def __set__(self, instance, value):
213221
self.field.set_state(instance, value)
214222

215223

216-
class FSMFieldMixin:
224+
class FSMFieldMixin(Field):
217225
descriptor_class = FSMFieldDescriptor
218226

219-
def __init__(self, *args, **kwargs):
227+
def __init__(self, *args, **kwargs) -> None:
220228
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)
222230
self.state_proxy = {} # state -> ProxyClsRef
223231

224232
state_choices = kwargs.pop("state_choices", None)
@@ -244,7 +252,7 @@ def deconstruct(self):
244252
def get_state(self, instance):
245253
# The state field may be deferred. We delegate the logic of figuring this out
246254
# 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]
248256

249257
def set_state(self, instance, state):
250258
instance.__dict__[self.name] = state
@@ -384,7 +392,7 @@ class FSMField(FSMFieldMixin, models.CharField):
384392
State Machine support for Django model as CharField
385393
"""
386394

387-
def __init__(self, *args, **kwargs):
395+
def __init__(self, *args, **kwargs) -> None:
388396
kwargs.setdefault("max_length", 50)
389397
super().__init__(*args, **kwargs)
390398

@@ -409,7 +417,7 @@ def set_state(self, instance, state):
409417
instance.__dict__[self.attname] = self.to_python(state)
410418

411419

412-
class ConcurrentTransitionMixin:
420+
class ConcurrentTransitionMixin(_Model):
413421
"""
414422
Protects a Model from undesirable effects caused by concurrently executed transitions,
415423
e.g. running the same transition multiple times at the same time, or running different
@@ -435,7 +443,7 @@ class ConcurrentTransitionMixin:
435443
state, thus practically negating their effect.
436444
"""
437445

438-
def __init__(self, *args, **kwargs):
446+
def __init__(self, *args, **kwargs) -> None:
439447
super().__init__(*args, **kwargs)
440448
self._update_initial_state()
441449

@@ -453,7 +461,7 @@ def _do_update(self, base_qs, using, pk_val, values, update_fields, forced_updat
453461
# state filter will be used to narrow down the standard filter checking only PK
454462
state_filter = {field.attname: self.__initial_states[field.attname] for field in filter_on}
455463

456-
updated = super()._do_update(
464+
updated = super()._do_update( # type: ignore[misc]
457465
base_qs=base_qs.filter(**state_filter),
458466
using=using,
459467
pk_val=pk_val,
@@ -518,7 +526,7 @@ def _change_state(instance, *args, **kwargs):
518526
return inner_transition
519527

520528

521-
def can_proceed(bound_method, check_conditions=True):
529+
def can_proceed(bound_method, check_conditions=True) -> bool:
522530
"""
523531
Returns True if model in state allows to call bound_method
524532
@@ -535,7 +543,7 @@ def can_proceed(bound_method, check_conditions=True):
535543
return meta.has_transition(current_state) and (not check_conditions or meta.conditions_met(self, current_state))
536544

537545

538-
def has_transition_perm(bound_method, user):
546+
def has_transition_perm(bound_method, user) -> bool:
539547
"""
540548
Returns True if model in state allows to call bound_method and user have rights on it
541549
"""
@@ -546,7 +554,7 @@ def has_transition_perm(bound_method, user):
546554
self = bound_method.__self__
547555
current_state = meta.field.get_state(self)
548556

549-
return (
557+
return bool(
550558
meta.has_transition(current_state)
551559
and meta.conditions_met(self, current_state)
552560
and meta.has_transition_perm(self, current_state, user)
@@ -559,7 +567,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
559567

560568

561569
class RETURN_VALUE(State):
562-
def __init__(self, *allowed_states):
570+
def __init__(self, *allowed_states) -> None:
563571
self.allowed_states = allowed_states if allowed_states else None
564572

565573
def get_state(self, model, transition, result, args=[], kwargs={}):
@@ -570,7 +578,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
570578

571579

572580
class GET_STATE(State):
573-
def __init__(self, func, states=None):
581+
def __init__(self, func, states=None) -> None:
574582
self.func = func
575583
self.allowed_states = states
576584

0 commit comments

Comments
 (0)