[Python-modules-commits] [django-fsm] 01/06: Import django-fsm_2.4.0.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Fri May 20 08:39:36 UTC 2016
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository django-fsm.
commit db9e9746e83da3f9dff579fecd3b8cb3193066b2
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Fri May 20 10:14:10 2016 +0200
Import django-fsm_2.4.0.orig.tar.gz
---
.checkignore | 2 +
.travis.yml | 17 ++-
CHANGELOG.rst | 101 +++++++++++++++
README.rst | 137 ++++++---------------
django_fsm/__init__.py | 62 +++++++---
.../management/commands/graph_transitions.py | 11 +-
django_fsm/tests/test_basic_transitions.py | 87 ++++++++++++-
setup.py | 8 +-
tests/testapp/models.py | 8 +-
tests/testapp/tests/test_multi_resultstate.py | 40 ++++++
tests/testapp/tests/test_string_field_parameter.py | 32 +++++
tox.ini | 64 +++++-----
12 files changed, 407 insertions(+), 162 deletions(-)
diff --git a/.checkignore b/.checkignore
new file mode 100644
index 0000000..e73d656
--- /dev/null
+++ b/.checkignore
@@ -0,0 +1,2 @@
+tests/*
+django_fsm/tests/*
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 0f4a34f..9497fc2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,15 @@
language: python
-env:
- - $TOX_ENV=py26
- - $TOX_ENV=py27
- - $TOX_ENV=py33
- - $TOX_ENV=py34
+sudo: false
+
+python:
+ - 2.6
+ - 2.7
+ - 3.3
+ - 3.4
+ - 3.5
+
install:
- pip install tox
+
script:
- - tox -e $TOX_ENV
+ - tox --skip-missing-interpreters
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..a804f97
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,101 @@
+Changelog
+=========
+
+django-fsm 2.4.0 2016-05-14
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- graph_transition commnad now works with multiple FSM's per model
+- Add ability to set target state from transition return value or callable
+
+
+django-fsm 2.3.0 2015-10-15
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Add source state shortcut '+' to specify transitions from all states except the target
+- Add object-level permission checks
+- Fix translated labels for graph of FSMIntegerField
+- Fix multiple signals for several transition decorators
+
+
+django-fsm 2.2.1 2015-04-27
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Improved exception message for unmet transition conditions.
+- Don't send post transition signal in case of no state changes on
+ exception
+- Allow empty string as correct state value
+- Improved graphviz fsm visualisation
+- Clean django 1.8 warnings
+
+django-fsm 2.2.0 2014-09-03
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Support for `class
+ substitution <http://schinckel.net/2013/06/13/django-proxy-model-state-machine/>`__
+ to proxy classes depending on the state
+- Added ConcurrentTransitionMixin with optimistic locking support
+- Default db\_index=True for FSMIntegerField removed
+- Graph transition code migrated to new graphviz library with python 3
+ support
+- Ability to change state on transition exception
+
+django-fsm 2.1.0 2014-05-15
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Support for attaching permission checks on model transitions
+
+django-fsm 2.0.0 2014-03-15
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Backward incompatible release
+- All public code import moved directly to django\_fsm package
+- Correct support for several @transitions decorator with different
+ source states and conditions on same method
+- save parameter from transition decorator removed
+- get\_available\_FIELD\_transitions return Transition data object
+ instead of tuple
+- Models got get\_available\_FIELD\_transitions, even if field
+ specified as string reference
+- New get\_all\_FIELD\_transitions method contributed to class
+
+django-fsm 1.6.0 2014-03-15
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- FSMIntegerField and FSMKeyField support
+
+django-fsm 1.5.1 2014-01-04
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Ad-hoc support for state fields from proxy and inherited models
+
+django-fsm 1.5.0 2013-09-17
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Python 3 compatibility
+
+django-fsm 1.4.0 2011-12-21
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Add graph\_transition command for drawing state transition picture
+
+django-fsm 1.3.0 2011-07-28
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Add direct field modification protection
+
+django-fsm 1.2.0 2011-03-23
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Add pre\_transition and post\_transition signals
+
+django-fsm 1.1.0 2011-02-22
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Add support for transition conditions
+- Allow multiple FSMField in one model
+- Contribute get\_available\_FIELD\_transitions for model class
+
+django-fsm 1.0.0 2010-10-12
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Initial public release
diff --git a/README.rst b/README.rst
index 7cbe137..1d52913 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,13 @@ Django friendly finite state machine support
|Build Status| |Downloads| |Gitter|
-django-fsm adds declarative states management for django models.
+django-fsm adds simple declarative states management for django models.
+
+If you need parallel task execution, view and background task code reuse
+over different flows - check my new project django-viewflow:
+
+https://github.com/viewflow/viewflow
+
Instead of adding some state field to a django model, and managing its
values by hand, you could use FSMState field and mark model methods with
@@ -28,10 +34,6 @@ FSM really helps to structure the code, especially when a new developer
comes to the project. FSM is most effective when you use it for some
sequential steps.
-If you need parallel task execution, view and background task code reuse
-over different flows - check my new project django-viewflow
-
-https://github.com/kmmbvnr/django-viewflow
Installation
------------
@@ -74,7 +76,8 @@ Use the ``transition`` decorator to annotate model methods
``source`` parameter accepts a list of states, or an individual state.
You can use ``*`` for source, to allow switching to ``target`` from any
-state.
+state. The ``field`` parameter accepts both a string attribute name or an
+actual field instance.
If calling publish() succeeds without raising an exception, the state
field will be changed, but not written to the database.
@@ -149,6 +152,30 @@ Note that calling
```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
+
+.. code:: python
+
+ from django_fsm import FSMField, transition, RETURN_VALUE, GET_STATE
+ @transition(field=state,
+ source='*',
+ target=RETURN_VALUE('for_moderators', 'published'))
+ def publish(self, is_public=False):
+ return 'need_moderation' if is_public else 'published'
+
+ @transition(
+ field=state,
+ source='for_moderators',
+ target=GET_STATE(
+ lambda self, allowed: 'published' if allowed else 'rejected',
+ states=['published', 'rejected']))
+ def moderate(self, allowed):
+ self.allowed=allowed
+
+
``custom`` properties
~~~~~~~~~~~~~~~~~~~~~
@@ -177,7 +204,7 @@ specific target state
@transition(field=state, source='new', target='published', on_error='failed')
def publish(self):
"""
- Some exceptio could happends here
+ Some exception could happen here
"""
``state_choices``
@@ -208,7 +235,7 @@ perform the transition
.. code:: python
@transition(field=state, source='*', target='publish',
- permission=lambda user: not user.has_perm('myapp.can_make_mistakes'))
+ permission=lambda instance, user: not user.has_perm('myapp.can_make_mistakes'))
def publish(self):
pass
@@ -323,7 +350,7 @@ Arguments sent with these signals:
**sender** The model class.
-**instance** The actual instance being procceed
+**instance** The actual instance being proceed
**name** Transition name
@@ -334,7 +361,7 @@ Arguments sent with these signals:
Optimistic locking
------------------
-``django-fsm`` provides optimistic locking mixin, to avoid concurent
+``django-fsm`` provides optimistic locking mixin, to avoid concurrent
model state changes. If model state was changed in database
``django_fsm.ConcurrentTransition`` exception would be raised on
model.save()
@@ -384,97 +411,13 @@ your ``INSTALLED_APPS``:
Changelog
---------
-django-fsm 2.3.0 2015-10-15
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Add source state shortcut '+' to specify transitions from all states except the target
-- Add object-level permission checks
-- Fix translated labels for graph of FSMIntegerField
-- Fix multiple signals for several transition decorators
-
-
-django-fsm 2.2.1 2015-04-27
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Improved exception message for unmet transition conditions.
-- Don't send post transiton signal in case of no state chnages on
- exception
-- Allow empty string as correct state value
-- Imporved graphviz fsm visualisation
-- Clean django 1.8 warnings
-
-django-fsm 2.2.0 2014-09-03
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Support for `class
- substitution <http://schinckel.net/2013/06/13/django-proxy-model-state-machine/>`__
- to proxy classes depending on the state
-- Added ConcurrentTransitionMixin with optimistic locking support
-- Default db\_index=True for FSMIntegerField removed
-- Graph transition code migrated to new graphviz library with python 3
- support
-- Ability to change state on transition exception
-
-django-fsm 2.1.0 2014-05-15
+django-fsm 2.4.0 2016-05-14
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Support for attaching permission checks on model transitions
+- graph_transition commnad now works with multiple FSM's per model
+- Add ability to set target state from transition return value or callable
-django-fsm 2.0.0 2014-03-15
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Backward incompatible release
-- All public code import moved directly to django\_fsm package
-- Correct support for several @transitions decorator with different
- source states and conditions on same method
-- save parameter from transition decorator removed
-- get\_available\_FIELD\_transitions return Transition data object
- instead of tuple
-- Models got get\_available\_FIELD\_transitions, even if field
- specified as string reference
-- New get\_all\_FIELD\_transitions method contributed to class
-
-django-fsm 1.6.0 2014-03-15
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- FSMIntegerField and FSMKeyField support
-
-django-fsm 1.5.1 2014-01-04
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Ad-hoc support for state fields from proxy and inherited models
-
-django-fsm 1.5.0 2013-09-17
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Python 3 compatibility
-
-django-fsm 1.4.0 2011-12-21
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Add graph\_transition command for drawing state transition picture
-
-django-fsm 1.3.0 2011-07-28
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Add direct field modification protection
-
-django-fsm 1.2.0 2011-03-23
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Add pre\_transition and post\_transition signals
-
-django-fsm 1.1.0 2011-02-22
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-- Add support for transition conditions
-- Allow multiple FSMField in one model
-- Contribute get\_available\_FIELD\_transitions for model class
-
-django-fsm 1.0.0 2010-10-12
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Initial public release
.. |Build Status| image:: https://travis-ci.org/kmmbvnr/django-fsm.svg?branch=master
:target: https://travis-ci.org/kmmbvnr/django-fsm
diff --git a/django_fsm/__init__.py b/django_fsm/__init__.py
index 56c8457..74e0274 100644
--- a/django_fsm/__init__.py
+++ b/django_fsm/__init__.py
@@ -4,7 +4,6 @@ State tracking functionality for django models
"""
import inspect
import sys
-import warnings
from functools import wraps
from django.db import models
@@ -65,6 +64,10 @@ class TransitionNotAllowed(Exception):
super(TransitionNotAllowed, self).__init__(*args, **kwargs)
+class InvalidResultState(Exception):
+ """Raised when we got invalid result state"""
+
+
class ConcurrentTransition(Exception):
"""
Raised when the transition cannot be executed because the
@@ -91,11 +94,7 @@ class Transition(object):
if not self.permission:
return True
elif callable(self.permission):
- try:
- return bool(self.permission(instance, user))
- except TypeError:
- warnings.warn('Callable permissions without instance parameter is depricated')
- return bool(self.permission(user))
+ return bool(self.permission(instance, user))
elif user.has_perm(self.permission, instance):
return True
elif user.has_perm(self.permission):
@@ -114,12 +113,8 @@ def get_available_FIELD_transitions(instance, field):
for name, transition in transitions.items():
meta = transition._django_fsm
-
- for state in [curr_state, '*', '+']:
- if state in meta.transitions:
- transition = meta.transitions[state]
- if all(map(lambda condition: condition(instance), transition.conditions)):
- yield transition
+ if meta.has_transition(curr_state) and meta.conditions_met(instance, curr_state):
+ yield meta.get_transition(curr_state)
def get_all_FIELD_transitions(instance, field):
@@ -322,6 +317,10 @@ class FSMFieldMixin(object):
try:
result = method(instance, *args, **kwargs)
if next_state is not None:
+ if hasattr(next_state, 'get_state'):
+ next_state = next_state.get_state(
+ instance, transition, result,
+ args=args, kwargs=kwargs)
self.set_proxy(instance, next_state)
self.set_state(instance, next_state)
except Exception as exc:
@@ -548,6 +547,39 @@ def has_transition_perm(bound_method, user):
im_self = getattr(bound_method, 'im_self', getattr(bound_method, '__self__'))
current_state = meta.field.get_state(im_self)
- return (meta.has_transition(current_state)
- and meta.conditions_met(im_self, current_state)
- and meta.has_transition_perm(im_self, current_state, user))
+ return (meta.has_transition(current_state) and
+ meta.conditions_met(im_self, current_state) and
+ meta.has_transition_perm(im_self, current_state, user))
+
+
+class State(object):
+ def get_state(self, model, transition, result, args=[], kwargs={}):
+ raise NotImplementedError
+
+
+class RETURN_VALUE(State):
+ def __init__(self, *allowed_states):
+ self.allowed_states = allowed_states if allowed_states else None
+
+ def get_state(self, model, transition, result, args=[], kwargs={}):
+ if self.allowed_states is not None:
+ if result not in self.allowed_states:
+ raise InvalidResultState(
+ '{} is not in list of allowed states\n{}'.format(
+ result, self.allowed_states))
+ return result
+
+
+class GET_STATE(State):
+ def __init__(self, func, states=None):
+ self.func = func
+ self.allowed_states = states
+
+ def get_state(self, model, transition, result, args=[], kwargs={}):
+ result_state = self.func(model, *args, **kwargs)
+ if self.allowed_states is not None:
+ if result_state not in self.allowed_states:
+ raise InvalidResultState(
+ '{} is not in list of allowed states\n{}'.format(
+ result, self.allowed_states))
+ return result_state
diff --git a/django_fsm/management/commands/graph_transitions.py b/django_fsm/management/commands/graph_transitions.py
index 0a2b493..bf4a636 100644
--- a/django_fsm/management/commands/graph_transitions.py
+++ b/django_fsm/management/commands/graph_transitions.py
@@ -42,8 +42,8 @@ def generate_dot(fields_data):
elif transition.source == '+':
any_except_targets.add((transition.target, transition.name))
else:
+ source_name = node_name(field, transition.source)
if transition.target is not None:
- source_name = node_name(field, transition.source)
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]
@@ -78,8 +78,8 @@ def generate_dot(fields_data):
# construct subgraph
opts = field.model._meta
subgraph = graphviz.Digraph(
- name="cluster_%s_%s" % (opts.app_label, opts.object_name),
- graph_attr={'label': "%s.%s" % (opts.app_label, opts.object_name)})
+ name="cluster_%s_%s_%s" % (opts.app_label, opts.object_name, field.name),
+ graph_attr={'label': "%s.%s.%s" % (opts.app_label, opts.object_name, field.name)})
final_states = targets - sources
for name, label in final_states:
@@ -88,8 +88,9 @@ def generate_dot(fields_data):
subgraph.node(name, label=label, shape='circle')
if field.default: # Adding initial state notation
if label == field.default:
- subgraph.node('.', shape='point')
- subgraph.edge('.', name)
+ initial_name = node_name(field, '_initial')
+ subgraph.node(name=initial_name, label='', shape='point')
+ subgraph.edge(initial_name, name)
for source_name, target_name, attrs in edges:
subgraph.edge(source_name, target_name, **dict(attrs))
diff --git a/django_fsm/tests/test_basic_transitions.py b/django_fsm/tests/test_basic_transitions.py
index 026f8aa..db8e3ac 100644
--- a/django_fsm/tests/test_basic_transitions.py
+++ b/django_fsm/tests/test_basic_transitions.py
@@ -32,6 +32,10 @@ class BlogPost(models.Model):
def moderate(self):
pass
+ @transition(source='+', target='blocked', field=state)
+ def block(self):
+ pass
+
@transition(source='*', target='', field=state)
def empty(self):
pass
@@ -87,6 +91,22 @@ class FSMFieldTest(TestCase):
self.model.moderate()
self.assertEqual(self.model.state, 'moderated')
+ def test_plus_shortcut_succeeds_for_other_source(self):
+ """Tests that the '+' shortcut succeeds for a source
+ other than the target.
+ """
+ self.assertTrue(can_proceed(self.model.block))
+ self.model.block()
+ self.assertEqual(self.model.state, 'blocked')
+
+ def test_plus_shortcut_fails_for_same_source(self):
+ """Tests that the '+' shortcut fails if the source
+ equals the target.
+ """
+ self.model.block()
+ self.assertFalse(can_proceed(self.model.block))
+ self.assertRaises(TransitionNotAllowed, self.model.block)
+
def test_empty_string_target(self):
self.model.empty()
self.assertEqual(self.model.state, '')
@@ -123,8 +143,68 @@ class TestFieldTransitionsInspect(TestCase):
def setUp(self):
self.model = BlogPost()
- def test_available_conditions(self):
- pass
+ def test_available_conditions_from_new(self):
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('new', 'published'),
+ ('new', 'removed'),
+ ('*', ''),
+ ('+', 'blocked')])
+ self.assertEqual(actual, expected)
+
+ def test_available_conditions_from_published(self):
+ self.model.publish()
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('published', None),
+ ('published', 'hidden'),
+ ('published', 'stolen'),
+ ('*', ''),
+ ('+', 'blocked')])
+ self.assertEqual(actual, expected)
+
+ def test_available_conditions_from_hidden(self):
+ self.model.publish()
+ self.model.hide()
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('hidden', 'stolen'),
+ ('*', ''),
+ ('+', 'blocked')])
+ self.assertEqual(actual, expected)
+
+ def test_available_conditions_from_stolen(self):
+ self.model.publish()
+ self.model.steal()
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('*', ''),
+ ('+', 'blocked')])
+ self.assertEqual(actual, expected)
+
+
+ def test_available_conditions_from_blocked(self):
+ self.model.block()
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('*', '')])
+ self.assertEqual(actual, expected)
+
+
+ def test_available_conditions_from_empty(self):
+ self.model.empty()
+ transitions = self.model.get_available_state_transitions()
+ actual = set((transition.source, transition.target) for transition in transitions)
+ expected = set([('*', 'moderated'),
+ ('*', ''),
+ ('+', 'blocked')])
+ self.assertEqual(actual, expected)
+
def test_all_conditions(self):
transitions = self.model.get_all_state_transitions()
@@ -137,5 +217,6 @@ class TestFieldTransitionsInspect(TestCase):
('published', 'hidden'),
('published', 'stolen'),
('hidden', 'stolen'),
- ('*', '')])
+ ('*', ''),
+ ('+', 'blocked')])
self.assertEqual(actual, expected)
diff --git a/setup.py b/setup.py
index c031b1d..68eb170 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ except IOError:
setup(
name='django-fsm',
- version='2.3.0',
+ version='2.4.0',
description='Django friendly finite state machine support.',
author='Mikhail Podgurskiy',
author_email='kmmbvnr at gmail.com',
@@ -24,10 +24,16 @@ setup(
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
+ "Framework :: Django",
+ "Framework :: Django :: 1.8",
+ "Framework :: Django :: 1.9",
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
'Framework :: Django',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
]
)
diff --git a/tests/testapp/models.py b/tests/testapp/models.py
index 4582164..49ce152 100644
--- a/tests/testapp/models.py
+++ b/tests/testapp/models.py
@@ -100,8 +100,12 @@ class BlogPost(models.Model):
def hide(self):
pass
- @transition(field=state, source='new', target='removed',
- on_error='failed', permission=lambda u: u.has_perm('testapp.can_remove_post'))
+ @transition(
+ field=state,
+ source='new',
+ target='removed',
+ on_error='failed',
+ permission=lambda self, u: u.has_perm('testapp.can_remove_post'))
def remove(self):
raise Exception('No rights to delete %s' % self)
diff --git a/tests/testapp/tests/test_multi_resultstate.py b/tests/testapp/tests/test_multi_resultstate.py
new file mode 100644
index 0000000..e7b385d
--- /dev/null
+++ b/tests/testapp/tests/test_multi_resultstate.py
@@ -0,0 +1,40 @@
+from django.db import models
+from django.test import TestCase
+from django_fsm import FSMField, transition, RETURN_VALUE, GET_STATE
+
+
+class MultiResultTest(models.Model):
+ state = FSMField(default='new')
+
+ @transition(
+ field=state,
+ source='new',
+ target=RETURN_VALUE('for_moderators', 'published'))
+ def publish(self, is_public=False):
+ return 'published' if is_public else 'for_moderators'
+
+ @transition(
+ field=state,
+ source='for_moderators',
+ target=GET_STATE(
+ lambda self, allowed: 'published' if allowed else 'rejected',
+ states=['published', 'rejected']
+ )
+ )
+ def moderate(self, allowed):
+ pass
+
+ class Meta:
+ app_label = 'testapp'
+
+
+class Test(TestCase):
+ def test_return_state_succeed(self):
+ instance = MultiResultTest()
+ instance.publish(is_public=True)
+ self.assertEqual(instance.state, 'published')
+
+ def test_get_state_succeed(self):
+ instance = MultiResultTest(state='for_moderators')
+ instance.moderate(allowed=False)
+ self.assertEqual(instance.state, 'rejected')
diff --git a/tests/testapp/tests/test_string_field_parameter.py b/tests/testapp/tests/test_string_field_parameter.py
new file mode 100644
index 0000000..9ba6e85
--- /dev/null
+++ b/tests/testapp/tests/test_string_field_parameter.py
@@ -0,0 +1,32 @@
+from django.db import models
+from django.test import TestCase
+from django_fsm import FSMField, transition
+
+
+class BlogPostWithStringField(models.Model):
+ state = FSMField(default='new')
+
+ @transition(field='state', source='new', target='published', conditions=[])
+ def publish(self):
+ pass
+
+ @transition(field='state', source='published', target='destroyed')
+ def destroy(self):
+ pass
+
+ @transition(field='state', source='published', target='review')
+ def review(self):
+ pass
+
+ class Meta:
+ app_label = 'testapp'
+
+
+class StringFieldTestCase(TestCase):
+ def setUp(self):
+ self.model = BlogPostWithStringField()
+
+ def test_initial_state(self):
+ self.assertEqual(self.model.state, 'new')
+ self.model.publish()
+ self.assertEqual(self.model.state, 'published')
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
index 6a99377..53e4e4b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,37 +1,35 @@
[tox]
-envlist = py26, py27, py33, py34
+envlist =
+ py26-{dj16}
+ py27-{dj16,dj18,dj19}
+ py33-{dj16,dj18}
+ py{34,35}-{dj18,dj19}
+skipsdist = True
[testenv]
-usedevelop = True
+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
+ 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-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
+ graphviz==0.4.10
+ pep8==1.7.0
+ pyflakes==1.0.0
+ ipdb==0.8.1
commands = python tests/manage.py {posargs:jenkins --pep8-max-line-length=150 --output-dir=reports/{envname}}
-deps = -r{toxinidir}/requirements.txt
- graphviz>=0.4
- django-jenkins
- django-guardian
- coverage
- pep8
- pyflakes
- ipdb
-
-
-[testenv:py26]
-deps = django==1.6.5
- ipython==2.1.0
- graphviz>=0.4
- django-jenkins==0.17.0
- django-guardian
- coverage
- pep8
- pyflakes
- ipdb
-
-[testenv:alpha]
-basepython = python3.4
-deps = git+https://github.com/django/django.git
- graphviz>=0.4
- django-jenkins
- django-guardian
- coverage
- pep8
- pyflakes
- ipdb
--
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