diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3b66e3..6d6cf2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ default_language_version: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files args: ["--maxkb=700"] @@ -18,17 +18,17 @@ repos: - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.16.0 hooks: - id: pyupgrade args: - "--py38-plus" - repo: https://github.com/adamchainz/django-upgrade - rev: 1.16.0 + rev: 1.18.0 hooks: - id: django-upgrade - args: [--target-version, "3.2"] + args: [--target-version, "4.2"] - repo: https://github.com/python-poetry/poetry @@ -43,7 +43,7 @@ repos: - id: poetry-export - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.4.8 hooks: - id: ruff-format - id: ruff diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ceee97d..16efb09 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,9 +1,19 @@ Changelog ========= -django-fsm 3.0.0 2024-03-25 +Unreleased +~~~~~~~~~~ + +- Add support for Django 5.1 +- Remove support for Django 3.2 +- Remove support for Django 4.0 +- Remove support for Django 4.1 + +django-fsm-2 3.0.0 2024-03-26 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +First release of the forked version of django-fsm + - Drop support for Python < 3.8. - Add support for python 3.11 - Add support for python 3.12 diff --git a/django_fsm/__init__.py b/django_fsm/__init__.py index 7d36acc..aa0779e 100644 --- a/django_fsm/__init__.py +++ b/django_fsm/__init__.py @@ -71,14 +71,13 @@ def name(self): def has_perm(self, instance, user): if not self.permission: return True - elif callable(self.permission): + if callable(self.permission): return bool(self.permission(instance, user)) - elif user.has_perm(self.permission, instance): + if user.has_perm(self.permission, instance): return True - elif user.has_perm(self.permission): + if user.has_perm(self.permission): return True - else: - return False + return False def __hash__(self): return hash(self.name) @@ -100,7 +99,7 @@ def get_available_FIELD_transitions(instance, field): curr_state = field.get_state(instance) transitions = field.transitions[instance.__class__] - for name, transition in transitions.items(): + for transition in transitions.values(): meta = transition._django_fsm if meta.has_transition(curr_state) and meta.conditions_met(instance, curr_state): yield meta.get_transition(curr_state) @@ -177,18 +176,19 @@ def conditions_met(self, instance, state): if transition is None: return False - elif transition.conditions is None: + + if transition.conditions is None: return True - else: - return all(map(lambda condition: condition(instance), transition.conditions)) + + return all(condition(instance) for condition in transition.conditions) def has_transition_perm(self, instance, state, user): transition = self.get_transition(state) if not transition: return False - else: - return transition.has_perm(instance, user) + + return transition.has_perm(instance, user) def next_state(self, current_state): transition = self.get_transition(current_state) @@ -341,7 +341,7 @@ def get_all_transitions(self, instance_cls): """ transitions = self.transitions[instance_cls] - for name, transition in transitions.items(): + for transition in transitions.values(): meta = transition._django_fsm for transition in meta.transitions.values(): @@ -565,7 +565,7 @@ def can_proceed(bound_method, check_conditions=True): conditions. """ if not hasattr(bound_method, "_django_fsm"): - raise TypeError("%s method is not transition" % bound_method.__func__.__name__) + raise TypeError(f"{bound_method.__func__.__name__} method is not transition") meta = bound_method._django_fsm self = bound_method.__self__ @@ -579,7 +579,7 @@ def has_transition_perm(bound_method, user): Returns True if model in state allows to call bound_method and user have rights on it """ if not hasattr(bound_method, "_django_fsm"): - raise TypeError("%s method is not transition" % bound_method.__func__.__name__) + raise TypeError(f"{bound_method.__func__.__name__} method is not transition") meta = bound_method._django_fsm self = bound_method.__self__ diff --git a/django_fsm/management/commands/graph_transitions.py b/django_fsm/management/commands/graph_transitions.py index 7f7b36a..2f7c97d 100644 --- a/django_fsm/management/commands/graph_transitions.py +++ b/django_fsm/management/commands/graph_transitions.py @@ -24,11 +24,10 @@ def node_name(field, state): def node_label(field, state): if isinstance(state, int) or (isinstance(state, bool) and hasattr(field, "choices")): return force_str(dict(field.choices).get(state)) - else: - return state + return state -def generate_dot(fields_data): +def generate_dot(fields_data): # noqa: C901 result = graphviz.Digraph() for field, model in fields_data: @@ -135,7 +134,7 @@ def add_arguments(self, parser): action="store", dest="layout", default="dot", - help=("Layout to be used by GraphViz for visualization. " "Layouts: %s." % " ".join(get_graphviz_layouts())), + help=f"Layout to be used by GraphViz for visualization. Layouts: {get_graphviz_layouts()}.", ) parser.add_argument("args", nargs="*", help=("[appname[.model[.field]]]")) diff --git a/poetry.lock b/poetry.lock index 9117358..c38d070 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "asgiref" @@ -519,4 +519,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "040bf0f7894980723ad0074242b91923c02e386c087be0ca6ca3ec8a76520012" +content-hash = "f46b8a5b9ffa4e59610be81ead75f64886b8138740c3538a88bf5912b140a652" diff --git a/pyproject.toml b/pyproject.toml index 06e3675..5661551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,9 @@ name = "django-fsm" version = "3.0.0" description = "Django friendly finite state machine support." -authors = ["Mikhail Podgurskiy "] +authors = [ + "Mikhail Podgurskiy ", +] license = "MIT License" readme = "README.md" homepage = "http://github.com/pfouque/django-fsm-2" @@ -15,10 +17,9 @@ classifiers = [ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", + "Framework :: Django :: 5.1", 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', @@ -32,7 +33,7 @@ packages = [{ include = "django_fsm" }] [tool.poetry.dependencies] python = "^3.8" -django = ">=3.2" +django = ">=4.2" [tool.poetry.group.graphviz.dependencies] graphviz = "*" @@ -55,13 +56,17 @@ target-version = "py38" fix = true [tool.ruff.lint] - +# select = ["ALL"] extend-select = [ "F", # Pyflakes "E", # pycodestyle "W", # pycodestyle "UP", # pyupgrade "I", # isort + "PERF", + "RET", + "C", + # "B", ] fixable = ["I"] diff --git a/tests/testapp/models.py b/tests/testapp/models.py index 7b92868..844a4f4 100644 --- a/tests/testapp/models.py +++ b/tests/testapp/models.py @@ -121,7 +121,7 @@ def hide(self): permission=lambda self, u: u.has_perm("testapp.can_remove_post"), ) def remove(self): - raise Exception("No rights to delete %s" % self) + raise Exception(f"No rights to delete {self}") @transition(field=state, source="new", target="restored", on_error="failed", permission=can_restore) def restore(self): diff --git a/tests/testapp/tests/test_custom_data.py b/tests/testapp/tests/test_custom_data.py index abf486b..8db857a 100644 --- a/tests/testapp/tests/test_custom_data.py +++ b/tests/testapp/tests/test_custom_data.py @@ -14,11 +14,11 @@ class BlogPostWithCustomData(models.Model): def publish(self): pass - @transition(field=state, source="published", target="destroyed", custom=dict(label="Destroy", type="manual")) + @transition(field=state, source="published", target="destroyed", custom={"label": "Destroy", "type": "manual"}) def destroy(self): pass - @transition(field=state, source="published", target="review", custom=dict(label="Periodic review", type="automated")) + @transition(field=state, source="published", target="review", custom={"label": "Periodic review", "type": "automated"}) def review(self): pass diff --git a/tox.ini b/tox.ini index e93a4f5..295bb44 100644 --- a/tox.ini +++ b/tox.ini @@ -1,19 +1,15 @@ [tox] envlist = - py{38,39,310}-dj32 - py{38,39,310}-dj40 - py{38,39,310,311}-dj41 py{38,39,310,311}-dj42 py{310,311,312}-dj50 + py{310,311,312}-dj51 skipsdist = True [testenv] deps = - dj32: Django==3.2 - dj40: Django==4.0 - dj41: Django==4.1 dj42: Django==4.2 dj50: Django==5.0 + dj51: Django==5.1b1 django-guardian==2.4.0 graphviz==0.20.1