PK!AgF?fsm/__init__.py"""Initializing package.""" from .exceptions import InvalidTransition # NOQA from .fsm import FiniteStateMachineMixin, BaseFiniteStateMachineMixin # NOQA __version__ = "2.0.0" PK!{Dfsm/exceptions.py__all__ = ["InvalidTransition"] class InvalidTransition(Exception): """Moving from an state to another is not possible.""" pass PK!II fsm/fsm.pyfrom .exceptions import InvalidTransition __all__ = ["FiniteStateMachineMixin", "BaseFiniteStateMachineMixin"] class BaseFiniteStateMachineMixin: """Base Mixin to add a state_machine behavior. Represents the state machine for the object. The states and transitions should be specified in the following way: .. code-block:: python state_machine = { 'some_state': '__all__' 'another_state': ('some_state', 'one_more_state') 'one_more_state': None } Requires the implementation of :code:`current_state` and :code:`set_state` """ state_machine = None def current_state(self): """Returns the current state in the FSM.""" raise NotImplementedError("Subclass must implement this method!") def set_state(self, state): """Update the internal state field. :param state: type depends on the definition of the states. :type state: str or int """ raise NotImplementedError("Subclass must implement this method!") def can_change(self, next_state): """Validates if the next_state can be executed or not. It uses the state_machine attribute in the class. """ valid_transitions = self.get_valid_transitions() if not valid_transitions: return False return next_state in valid_transitions def get_valid_transitions(self): """Return possible states to whom a product can transition. :returns: valid transitions :rtype: tuple or list """ current = self.current_state() valid_transitions = self.state_machine[current] if valid_transitions == "__all__": return self.state_machine.keys() return self.state_machine[current] def on_before_change_state(self, previous_state, next_state, **kwargs): """Called before a state changes. :param previous_state: type depends on the definition of the states. :type previous_state: str or int :param next_state: type depends on the definition of the states. :type next_state: str or int """ pass def on_change_state(self, previous_state, next_state, **kwargs): """Called after a state changes. :param previous_state: type depends on the definition of the states. :type previous_state: str or int :param next_state: type depends on the definition of the states. :type next_state: str or int """ pass def change_state(self, next_state, **kwargs): """Performs a transition from current state to the given next state if possible. Callbacks will be exacuted before an after changing the state. Specific state callbacks will also be called if they are implemented in the subclass. :param next_state: where the state must go :type next_state: str or int :returns: new state. :rtype: str or int :raises InvalidTransition: If transitioning is not possible """ previous_state = self.current_state() if not self.can_change(next_state): msg = "The transition from {0} to {1} is not valid".format( previous_state, next_state ) raise InvalidTransition(msg) name = "pre_{0}".format(next_state) callback = getattr(self, name, None) if callback: callback(**kwargs) self.on_before_change_state(previous_state, next_state, **kwargs) self.set_state(next_state) name = "post_{0}".format(next_state) callback = getattr(self, name, None) if callback: callback(**kwargs) self.on_change_state(previous_state, next_state, **kwargs) return next_state class FiniteStateMachineMixin(BaseFiniteStateMachineMixin): """A drop in implementation. Ready to be used. Replace :code:`FIELD_NAME` in order to automatically retrieve or set from a different field. In order to use with django, just add a field :code:`state` or as defined in :code:`FIELD_NAME` and remember to use :code:`change_state` instead of simply assigning it """ FIELD_NAME = "state" def current_state(self): return getattr(self, self.FIELD_NAME) def set_state(self, state): setattr(self, self.FIELD_NAME, state) PK!SO1))fsmpy-2.0.0.dist-info/LICENSEMIT License Copyright (c) 2018 Santiago Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HMWXfsmpy-2.0.0.dist-info/WHEEL A н#Z@Z|Jl~6蓅 Λ MU4[PYBpYD*Mͯ#ڪ/̚?ݭ'%nPK!H1ׅJfsmpy-2.0.0.dist-info/METADATAXmo8_A䋝’6mB^Nwk^PWKKcD $vIɒ'ޗNEP|}83hSMTL<KAH*^c(8&EQ,)Qj iKs bD",~F{WOVXPdgJ 2$5 $e' P%‡4$nqsĬ%aJb h+HEd" E$>"."qcBn5brV xdjaKC j2&gy. ICkz"Aቤ':h7^Ib_|p7˽)^M$M,0tci;_*yV W n/LKi`)C*]HE1= s)/_= J76w-銕R$bĪ&itd+wH{HU乐bSExbT=kSQ[` 4K0I+DZܷ>Uz`RJ@?)DTl4+OSt=ݺ1˸QdJ%rQA6LpL~.XWv*X{el. UsXQ9NMlȲ$PKi }Yշ*XzL],&R vkh=.F0V2o>IOu~\II%TWC$DOWzK屉nj{M(зTby2뚦2CCz)u 7ğ"A8\F[(bϊyL!)U7k ;;#N vag5~!ZN EqEpŬ !rI.G?1Q9D"PM5W9+1O':네34:6"Ǡ`CED# 0 UD(,d!8ε7K+\f ҭW-6?jİ lU.z-p~#єZA:l`D>Aa!$J--d\wƝ$HxSR8lƜv{H2Y 6/ٌNLc2|>".mʩO?|ni=pR&pI;3w3z7K'z9xqþ$p~V2Izf|E )GlmPta7oɮm=@ӝ" ag~\1oU.X$| u”#EEeŤFc7[j?G,OS)Y;Ktv6]]O'|jV1Bla PK!HUfsmpy-2.0.0.dist-info/RECORDur0}%L&,$-HKjA,AO