Skip to content

Commit 4445f72

Browse files
committed
step 1
1 parent 4639a59 commit 4445f72

File tree

2 files changed

+37
-29
lines changed

2 files changed

+37
-29
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: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import inspect
88
from functools import partialmethod
99
from functools import wraps
10+
from typing import TYPE_CHECKING
11+
from typing import Any
12+
from typing import Callable
1013

1114
from django.apps import apps as django_apps
1215
from django.db import models
@@ -32,11 +35,16 @@
3235
"RETURN_VALUE",
3336
]
3437

38+
if TYPE_CHECKING:
39+
_Model = models.Model
40+
else:
41+
_Model = object
42+
3543

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

39-
def __init__(self, *args, **kwargs):
47+
def __init__(self, *args, **kwargs) -> None:
4048
self.object = kwargs.pop("object", None)
4149
self.method = kwargs.pop("method", None)
4250
super().__init__(*args, **kwargs)
@@ -55,7 +63,7 @@ class ConcurrentTransition(Exception):
5563

5664

5765
class Transition:
58-
def __init__(self, method, source, target, on_error, conditions, permission, custom):
66+
def __init__(self, method: Callable, source, target, on_error, conditions, permission, custom) -> None:
5967
self.method = method
6068
self.source = source
6169
self.target = target
@@ -65,10 +73,10 @@ def __init__(self, method, source, target, on_error, conditions, permission, cus
6573
self.custom = custom
6674

6775
@property
68-
def name(self):
76+
def name(self) -> str:
6977
return self.method.__name__
7078

71-
def has_perm(self, instance, user):
79+
def has_perm(self, instance, user) -> bool:
7280
if not self.permission:
7381
return True
7482
if callable(self.permission):
@@ -127,9 +135,9 @@ class FSMMeta:
127135
Models methods transitions meta information
128136
"""
129137

130-
def __init__(self, field, method):
138+
def __init__(self, field, method) -> None:
131139
self.field = field
132-
self.transitions = {} # source -> Transition
140+
self.transitions: dict[str, Any] = {} # source -> Transition
133141

134142
def get_transition(self, source):
135143
transition = self.transitions.get(source, None)
@@ -139,7 +147,7 @@ def get_transition(self, source):
139147
transition = self.transitions.get("+", None)
140148
return transition
141149

142-
def add_transition(self, method, source, target, on_error=None, conditions=[], permission=None, custom={}):
150+
def add_transition(self, method, source, target, on_error=None, conditions=[], permission=None, custom={}) -> None:
143151
if source in self.transitions:
144152
raise AssertionError(f"Duplicate transition for {source} state")
145153

@@ -153,7 +161,7 @@ def add_transition(self, method, source, target, on_error=None, conditions=[], p
153161
custom=custom,
154162
)
155163

156-
def has_transition(self, state):
164+
def has_transition(self, state) -> bool:
157165
"""
158166
Lookup if any transition exists from current model state using current method
159167
"""
@@ -168,7 +176,7 @@ def has_transition(self, state):
168176

169177
return False
170178

171-
def conditions_met(self, instance, state):
179+
def conditions_met(self, instance, state) -> bool:
172180
"""
173181
Check if all conditions have been met
174182
"""
@@ -182,13 +190,13 @@ def conditions_met(self, instance, state):
182190

183191
return all(condition(instance) for condition in transition.conditions)
184192

185-
def has_transition_perm(self, instance, state, user):
193+
def has_transition_perm(self, instance, state, user) -> bool:
186194
transition = self.get_transition(state)
187195

188196
if not transition:
189197
return False
190-
191-
return transition.has_perm(instance, user)
198+
else:
199+
return bool(transition.has_perm(instance, user))
192200

193201
def next_state(self, current_state):
194202
transition = self.get_transition(current_state)
@@ -208,15 +216,15 @@ def exception_state(self, current_state):
208216

209217

210218
class FSMFieldDescriptor:
211-
def __init__(self, field):
219+
def __init__(self, field) -> None:
212220
self.field = field
213221

214222
def __get__(self, instance, type=None):
215223
if instance is None:
216224
return self
217225
return self.field.get_state(instance)
218226

219-
def __set__(self, instance, value):
227+
def __set__(self, instance, value) -> None:
220228
if self.field.protected and self.field.name in instance.__dict__:
221229
raise AttributeError(f"Direct {self.field.name} modification is not allowed")
222230

@@ -225,12 +233,12 @@ def __set__(self, instance, value):
225233
self.field.set_state(instance, value)
226234

227235

228-
class FSMFieldMixin:
236+
class FSMFieldMixin(Field):
229237
descriptor_class = FSMFieldDescriptor
230238

231-
def __init__(self, *args, **kwargs):
239+
def __init__(self, *args, **kwargs) -> None:
232240
self.protected = kwargs.pop("protected", False)
233-
self.transitions = {} # cls -> (transitions name -> method)
241+
self.transitions: dict[Any, dict[str, Any]] = {} # cls -> (transitions name -> method)
234242
self.state_proxy = {} # state -> ProxyClsRef
235243

236244
state_choices = kwargs.pop("state_choices", None)
@@ -256,7 +264,7 @@ def deconstruct(self):
256264
def get_state(self, instance):
257265
# The state field may be deferred. We delegate the logic of figuring this out
258266
# and loading the deferred field on-demand to Django's built-in DeferredAttribute class.
259-
return DeferredAttribute(self).__get__(instance)
267+
return DeferredAttribute(self).__get__(instance) # type: ignore[attr-defined]
260268

261269
def set_state(self, instance, state):
262270
instance.__dict__[self.name] = state
@@ -396,7 +404,7 @@ class FSMField(FSMFieldMixin, models.CharField):
396404
State Machine support for Django model as CharField
397405
"""
398406

399-
def __init__(self, *args, **kwargs):
407+
def __init__(self, *args, **kwargs) -> None:
400408
kwargs.setdefault("max_length", 50)
401409
super().__init__(*args, **kwargs)
402410

@@ -421,7 +429,7 @@ def set_state(self, instance, state):
421429
instance.__dict__[self.attname] = self.to_python(state)
422430

423431

424-
class FSMModelMixin:
432+
class FSMModelMixin(_Model):
425433
"""
426434
Mixin that allows refresh_from_db for models with fsm protected fields
427435
"""
@@ -448,7 +456,7 @@ def refresh_from_db(self, *args, **kwargs):
448456
super().refresh_from_db(*args, **kwargs)
449457

450458

451-
class ConcurrentTransitionMixin:
459+
class ConcurrentTransitionMixin(_Model):
452460
"""
453461
Protects a Model from undesirable effects caused by concurrently executed transitions,
454462
e.g. running the same transition multiple times at the same time, or running different
@@ -474,7 +482,7 @@ class ConcurrentTransitionMixin:
474482
state, thus practically negating their effect.
475483
"""
476484

477-
def __init__(self, *args, **kwargs):
485+
def __init__(self, *args, **kwargs) -> None:
478486
super().__init__(*args, **kwargs)
479487
self._update_initial_state()
480488

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

495-
updated = super()._do_update(
503+
updated = super()._do_update( # type: ignore[misc]
496504
base_qs=base_qs.filter(**state_filter),
497505
using=using,
498506
pk_val=pk_val,
@@ -557,7 +565,7 @@ def _change_state(instance, *args, **kwargs):
557565
return inner_transition
558566

559567

560-
def can_proceed(bound_method, check_conditions=True):
568+
def can_proceed(bound_method, check_conditions=True) -> bool:
561569
"""
562570
Returns True if model in state allows to call bound_method
563571
@@ -574,7 +582,7 @@ def can_proceed(bound_method, check_conditions=True):
574582
return meta.has_transition(current_state) and (not check_conditions or meta.conditions_met(self, current_state))
575583

576584

577-
def has_transition_perm(bound_method, user):
585+
def has_transition_perm(bound_method, user) -> bool:
578586
"""
579587
Returns True if model in state allows to call bound_method and user have rights on it
580588
"""
@@ -585,7 +593,7 @@ def has_transition_perm(bound_method, user):
585593
self = bound_method.__self__
586594
current_state = meta.field.get_state(self)
587595

588-
return (
596+
return bool(
589597
meta.has_transition(current_state)
590598
and meta.conditions_met(self, current_state)
591599
and meta.has_transition_perm(self, current_state, user)
@@ -598,7 +606,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
598606

599607

600608
class RETURN_VALUE(State):
601-
def __init__(self, *allowed_states):
609+
def __init__(self, *allowed_states) -> None:
602610
self.allowed_states = allowed_states if allowed_states else None
603611

604612
def get_state(self, model, transition, result, args=[], kwargs={}):
@@ -609,7 +617,7 @@ def get_state(self, model, transition, result, args=[], kwargs={}):
609617

610618

611619
class GET_STATE(State):
612-
def __init__(self, func, states=None):
620+
def __init__(self, func, states=None) -> None:
613621
self.func = func
614622
self.allowed_states = states
615623

0 commit comments

Comments
 (0)