[Python-modules-commits] [django-fsm] 01/01: Import django-fsm_2.5.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Mon Mar 6 11:27:44 UTC 2017


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch upstream
in repository django-fsm.

commit ea76bf56c8d55449bc65ca6ccd5fec3e234191b1
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Mon Mar 6 11:26:41 2017 +0100

    Import django-fsm_2.5.0.orig.tar.gz
---
 CHANGELOG.rst                                      |  9 +++
 README.rst                                         | 30 ++++-----
 django_fsm/__init__.py                             | 20 +++---
 .../management/commands/graph_transitions.py       | 71 +++++++++++++++-------
 setup.cfg                                          |  2 +
 setup.py                                           |  3 +-
 tests/testapp/tests/test_multi_resultstate.py      | 29 +++++++++
 tox.ini                                            | 38 ++++++------
 8 files changed, 139 insertions(+), 63 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index a804f97..9d38bd5 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,15 @@
 Changelog
 =========
 
+django-fsm 2.5.0 2017-03-04
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- graph_transition command fix for django 1.10
+- graph_transition command supports GET_STATE targets
+- signal data extended with method args/kwargs and field
+- sets allowed to be passed to the transition decorator
+
+
 django-fsm 2.4.0 2016-05-14
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/README.rst b/README.rst
index 1d52913..31cca8b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
 Django friendly finite state machine support
 ============================================
 
-|Build Status| |Downloads| |Gitter|
+|Build Status| |Gitter|
 
 django-fsm adds simple declarative states management for django models.
 
@@ -149,13 +149,13 @@ direct state field modification.
     model.state = 'invalid' # Raises AttributeError
 
 Note that calling
-```refresh_from_db`` <https://docs.djangoproject.com/en/1.8/ref/models/instances/#django.db.models.Model.refresh_from_db>`__
+`refresh_from_db <https://docs.djangoproject.com/en/1.8/ref/models/instances/#django.db.models.Model.refresh_from_db>`_
 on a model instance with a protected FSMField will cause an exception.
 
 `target`
 ~~~~~~~~
 
-`target` state parameter could points to the specific state or `django_fsm.State` implementation
+`target` state parameter could point to a specific state or `django_fsm.State` implementation
 
 .. code:: python
           
@@ -164,7 +164,7 @@ on a model instance with a protected FSMField will cause an exception.
                 source='*',
                 target=RETURN_VALUE('for_moderators', 'published'))
     def publish(self, is_public=False):
-        return 'need_moderation' if is_public else 'published'
+        return 'for_moderators' if is_public else 'published'
 
     @transition(
         field=state,
@@ -229,8 +229,8 @@ Permissions
 It is common to have permissions attached to each model transition.
 ``django-fsm`` handles this with ``permission`` keyword on the
 ``transition`` decorator. ``permission`` accepts a permission string, or
-callable that expects ``user`` argument and returns True if user can
-perform the transition
+callable that expects ``instance`` and ``user`` arguments and returns
+True if user can perform the transition.
 
 .. code:: python
 
@@ -374,10 +374,10 @@ model.save()
         state = FSMField(default='new')
 
 For guaranteed protection against race conditions caused by concurrently
-executed transitions, make sure: \* Your transitions do not have any
-side effects except for changes in the database, \* You always run the
-save() method on the object within ``django.db.transaction.atomic()``
-block.
+executed transitions, make sure:
+
+- Your transitions do not have any side effects except for changes in the database,
+- You always run the save() method on the object within ``django.db.transaction.atomic()`` block.
 
 Following these recommendations, you can rely on
 ConcurrentTransitionMixin to cause a rollback of all the changes that
@@ -411,17 +411,17 @@ your ``INSTALLED_APPS``:
 Changelog
 ---------
 
-django-fsm 2.4.0 2016-05-14
+django-fsm 2.5.0 2017-03-04
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- graph_transition commnad now works with multiple  FSM's per model
-- Add ability to set target state from transition return value or callable
+- graph_transition command fix for django 1.10
+- graph_transition command supports GET_STATE targets
+- signal data extended with method args/kwargs and field
+- sets allowed to be passed to the transition decorator
 
 
 
 .. |Build Status| image:: https://travis-ci.org/kmmbvnr/django-fsm.svg?branch=master
    :target: https://travis-ci.org/kmmbvnr/django-fsm
-.. |Downloads| image:: https://img.shields.io/pypi/dm/django-fsm.svg
-   :target: https://pypi.python.org/pypi/django-fsm
 .. |Gitter| image:: https://badges.gitter.im/Join%20Chat.svg
    :target: https://gitter.im/kmmbvnr/django-fsm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
diff --git a/django_fsm/__init__.py b/django_fsm/__init__.py
index 74e0274..d93cff9 100644
--- a/django_fsm/__init__.py
+++ b/django_fsm/__init__.py
@@ -245,7 +245,7 @@ class FSMFieldMixin(object):
         state_choices = kwargs.pop('state_choices', None)
         choices = kwargs.get('choices', None)
         if state_choices is not None and choices is not None:
-            raise ValueError('Use one of choices or state_choces value')
+            raise ValueError('Use one of choices or state_choices value')
 
         if state_choices is not None:
             choices = []
@@ -308,8 +308,11 @@ class FSMFieldMixin(object):
             'sender': instance.__class__,
             'instance': instance,
             'name': method_name,
+            'field': meta.field,
             'source': current_state,
-            'target': next_state
+            'target': next_state,
+            'method_args' : args,
+            'method_kwargs' : kwargs
         }
 
         pre_transition.send(**signal_kwargs)
@@ -321,6 +324,7 @@ class FSMFieldMixin(object):
                     next_state = next_state.get_state(
                         instance, transition, result,
                         args=args, kwargs=kwargs)
+                    signal_kwargs['target'] = next_state
                 self.set_proxy(instance, next_state)
                 self.set_state(instance, next_state)
         except Exception as exc:
@@ -349,10 +353,10 @@ class FSMFieldMixin(object):
             for transition in meta.transitions.values():
                 yield transition
 
-    def contribute_to_class(self, cls, name, virtual_only=False):
+    def contribute_to_class(self, cls, name, **kwargs):
         self.base_cls = cls
 
-        super(FSMFieldMixin, self).contribute_to_class(cls, name, virtual_only=virtual_only)
+        super(FSMFieldMixin, self).contribute_to_class(cls, name, **kwargs)
         setattr(cls, self.name, self.descriptor_class(self))
         setattr(cls, 'get_all_{0}_transitions'.format(self.name),
                 curry(get_all_FIELD_transitions, field=self))
@@ -500,7 +504,7 @@ def transition(field, source='*', target=None, on_error=None, conditions=[], per
             fsm_meta = FSMMeta(field=field, method=func)
             setattr(func, '_django_fsm', fsm_meta)
 
-        if isinstance(source, (list, tuple)):
+        if isinstance(source, (list, tuple, set)):
             for state in source:
                 func._django_fsm.add_transition(func, state, target, on_error, conditions, permission, custom)
         else:
@@ -526,7 +530,8 @@ 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.im_func.__name__)
+        im_func = getattr(bound_method, 'im_func', getattr(bound_method, '__func__'))
+        raise TypeError('%s method is not transition' % im_func.__name__)
 
     meta = bound_method._django_fsm
     im_self = getattr(bound_method, 'im_self', getattr(bound_method, '__self__'))
@@ -541,7 +546,8 @@ 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.im_func.__name__)
+        im_func = getattr(bound_method, 'im_func', getattr(bound_method, '__func__'))
+        raise TypeError('%s method is not transition' % im_func.__name__)
 
     meta = bound_method._django_fsm
     im_self = getattr(bound_method, 'im_self', getattr(bound_method, '__self__'))
diff --git a/django_fsm/management/commands/graph_transitions.py b/django_fsm/management/commands/graph_transitions.py
index bf4a636..8eea6bf 100644
--- a/django_fsm/management/commands/graph_transitions.py
+++ b/django_fsm/management/commands/graph_transitions.py
@@ -5,7 +5,7 @@ from optparse import make_option
 from django.core.management.base import BaseCommand
 from django.utils.encoding import smart_text
 
-from django_fsm import FSMFieldMixin
+from django_fsm import FSMFieldMixin, GET_STATE, RETURN_VALUE
 
 try:
     from django.db.models import get_apps, get_app, get_models, get_model
@@ -14,6 +14,10 @@ except ImportError:
     from django.apps import apps
     NEW_META_API = True
 
+from django import VERSION
+
+HAS_ARGPARSE = VERSION >= (1, 10)
+
 
 def all_fsm_fields_data(model):
     if NEW_META_API:
@@ -44,18 +48,13 @@ def generate_dot(fields_data):
             else:
                 source_name = node_name(field, transition.source)
                 if transition.target is not None:
-                    target_name = node_name(field, transition.target)
-                    if isinstance(transition.source, int):
-                        source_label = [smart_text(name[1]) for name in field.choices if name[0] == transition.source][0]
+                    if isinstance(transition.target, GET_STATE) or isinstance(transition.target, RETURN_VALUE):
+                        for transition_target_index, transition_target in enumerate(transition.target.allowed_states):
+                            add_transition(transition.source, transition_target, transition.name,
+                                           source_name, field, sources, targets, edges)
                     else:
-                        source_label = transition.source
-                    sources.add((source_name, source_label))
-                    if isinstance(transition.target, int):
-                        target_label = [smart_text(name[1]) for name in field.choices if name[0] == transition.target][0]
-                    else:
-                        target_label = transition.target
-                    targets.add((target_name, target_label))
-                    edges.add((source_name, target_name, (('label', transition.name),)))
+                        add_transition(transition.source, transition.target, transition.name,
+                                       source_name, field, sources, targets, edges)
             if transition.on_error:
                 on_error_name = node_name(field, transition.on_error)
                 targets.add((on_error_name, transition.on_error))
@@ -99,21 +98,49 @@ def generate_dot(fields_data):
     return result
 
 
+def add_transition(transition_source, transition_target, transition_name, source_name, field, sources, targets, edges):
+    target_name = node_name(field, transition_target)
+    if isinstance(transition_source, int):
+        source_label = [smart_text(name[1]) for name in field.choices if name[0] == transition_source][0]
+    else:
+        source_label = transition_source
+    sources.add((source_name, source_label))
+    if isinstance(transition_target, int):
+        target_label = [smart_text(name[1]) for name in field.choices if name[0] == transition_target][0]
+    else:
+        target_label = transition_target
+    targets.add((target_name, target_label))
+    edges.add((source_name, target_name, (('label', transition_name),)))
+
+
 class Command(BaseCommand):
     requires_system_checks = True
 
-    option_list = BaseCommand.option_list + (
-        make_option('--output', '-o', action='store', dest='outputfile',
-                    help=('Render output file. Type of output dependent on file extensions. '
-                          'Use png or jpg to render graph to image.')),
-        # NOQA
-        make_option('--layout', '-l', action='store', dest='layout', default='dot',
-                    help=('Layout to be used by GraphViz for visualization. '
-                          'Layouts: circo dot fdp neato nop nop1 nop2 twopi')),
-    )
+    if not HAS_ARGPARSE:
+        option_list = BaseCommand.option_list + (
+            make_option('--output', '-o', action='store', dest='outputfile',
+                        help=('Render output file. Type of output dependent on file extensions. '
+                              'Use png or jpg to render graph to image.')),
+            # NOQA
+            make_option('--layout', '-l', action='store', dest='layout', default='dot',
+                        help=('Layout to be used by GraphViz for visualization. '
+                              'Layouts: circo dot fdp neato nop nop1 nop2 twopi')),
+        )
+        args = "[appname[.model[.field]]]"
+    else:
+        def add_arguments(self, parser):
+            parser.add_argument(
+                '--output', '-o', action='store', dest='outputfile',
+                help=('Render output file. Type of output dependent on file extensions. '
+                      'Use png or jpg to render graph to image.'))
+            parser.add_argument(
+                '--layout', '-l', action='store', dest='layout', default='dot',
+                help=('Layout to be used by GraphViz for visualization. '
+                      'Layouts: circo dot fdp neato nop nop1 nop2 twopi'))
+            parser.add_argument('args', nargs='*',
+                                help=('[appname[.model[.field]]]'))
 
     help = ("Creates a GraphViz dot file with transitions for selected fields")
-    args = "[appname[.model[.field]]]"
 
     def render_output(self, graph, **options):
         filename, format = options['outputfile'].rsplit('.', 1)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..2a9acf1
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal = 1
diff --git a/setup.py b/setup.py
index 68eb170..61eb245 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ except IOError:
 
 setup(
     name='django-fsm',
-    version='2.4.0',
+    version='2.5.0',
     description='Django friendly finite state machine support.',
     author='Mikhail Podgurskiy',
     author_email='kmmbvnr at gmail.com',
@@ -27,6 +27,7 @@ setup(
         "Framework :: Django",
         "Framework :: Django :: 1.8",
         "Framework :: Django :: 1.9",
+        "Framework :: Django :: 1.10",
         'Programming Language :: Python',
         'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
diff --git a/tests/testapp/tests/test_multi_resultstate.py b/tests/testapp/tests/test_multi_resultstate.py
index e7b385d..ebdb9c4 100644
--- a/tests/testapp/tests/test_multi_resultstate.py
+++ b/tests/testapp/tests/test_multi_resultstate.py
@@ -1,6 +1,7 @@
 from django.db import models
 from django.test import TestCase
 from django_fsm import FSMField, transition, RETURN_VALUE, GET_STATE
+from django_fsm.signals import pre_transition, post_transition
 
 
 class MultiResultTest(models.Model):
@@ -38,3 +39,31 @@ class Test(TestCase):
         instance = MultiResultTest(state='for_moderators')
         instance.moderate(allowed=False)
         self.assertEqual(instance.state, 'rejected')
+
+
+class TestSignals(TestCase):
+    def setUp(self):
+        self.pre_transition_called = False
+        self.post_transition_called = False
+        pre_transition.connect(self.on_pre_transition, sender=MultiResultTest)
+        post_transition.connect(self.on_post_transition, sender=MultiResultTest)
+
+    def on_pre_transition(self, sender, instance, name, source, target, **kwargs):
+        self.assertEqual(instance.state, source)
+        self.pre_transition_called = True
+
+    def on_post_transition(self, sender, instance, name, source, target, **kwargs):
+        self.assertEqual(instance.state, target)
+        self.post_transition_called = True
+
+    def test_signals_called_with_get_state(self):
+        instance = MultiResultTest(state='for_moderators')
+        instance.moderate(allowed=False)
+        self.assertTrue(self.pre_transition_called)
+        self.assertTrue(self.post_transition_called)
+
+    def test_signals_called_with_return_value(self):
+        instance = MultiResultTest()
+        instance.publish(is_public=True)
+        self.assertTrue(self.pre_transition_called)
+        self.assertTrue(self.post_transition_called)
diff --git a/tox.ini b/tox.ini
index 53e4e4b..6856c4b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,33 +1,35 @@
 [tox]
 envlist =
-    py26-{dj16}
-    py27-{dj16,dj18,dj19}
-    py33-{dj16,dj18}
-    py{34,35}-{dj18,dj19}
+    py26-dj{16}
+    py27-dj{16,18,19,110,111}
+    py33-dj{16,18}
+    py{34,35}-dj{18,19,110,111}
 skipsdist = True
 
 [testenv]
-basepython =
-    py26: python2.6
-    py27: python2.7
-    py33: python3.3
-    py34: python3.4
-    py35: python3.5
 deps =
     py26: ipython==2.1.0
-    {py27,py32,py33,py34,py35}: ipython==4.1.1    
+    {py27,py32,py33,py34,py35}: ipython==4.1.1
     dj16: Django==1.6.11
     dj16: django-jenkins==0.17.0
     dj16: coverage<=3.999
     dj16: django-guardian==1.3.2
-    dj18: Django==1.8.9
+    dj18: Django==1.8.13
     dj18: django-jenkins==0.18.1
-    dj18: coverage==4.0.3
-    dj18: django-guardian==1.4.1
-    dj19: Django==1.9.2
-    dj19: django-jenkins==0.18.1
-    dj19: coverage==4.0.3
-    dj19: django-guardian==1.4.1
+    dj18: coverage==4.1
+    dj18: django-guardian==1.4.4
+    dj19: Django==1.9.7
+    dj19: django-jenkins==0.19.0
+    dj19: coverage==4.1
+    dj19: django-guardian==1.4.4
+    dj110: Django==1.10.5
+    dj110: django-jenkins==0.19.0
+    dj110: coverage==4.1
+    dj110: django-guardian==1.4.4
+    dj111: Django==1.11b1
+    dj111: django-jenkins==0.110.0
+    dj111: coverage==4.3.4
+    dj111: django-guardian==1.4.6
     graphviz==0.4.10
     pep8==1.7.0
     pyflakes==1.0.0

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-fsm.git



More information about the Python-modules-commits mailing list