Skip to main content
# decorstate

[![Build Status](https://travis-ci.org/ahawker/decorstate.svg?branch=master)](https://travis-ci.org/ahawker/decorstate)
[![Test Coverage](https://codeclimate.com/github/ahawker/decorstate/badges/coverage.svg)](https://codeclimate.com/github/ahawker/decorstate/coverage)
[![Code Climate](https://codeclimate.com/github/ahawker/decorstate/badges/gpa.svg)](https://codeclimate.com/github/ahawker/decorstate)
[![Issue Count](https://codeclimate.com/github/ahawker/decorstate/badges/issue_count.svg)](https://codeclimate.com/github/ahawker/decorstate)

[![PyPI Version](https://badge.fury.io/py/decorstate.svg)](https://badge.fury.io/py/decorstate)
[![PyPI Versions](https://img.shields.io/pypi/pyversions/decorstate.svg)](https://pypi.python.org/pypi/decorstate)
[![PyPI Downloads](https://img.shields.io/pypi/dm/decorstate.svg)](https://pypi.python.org/pypi/decorstate)

Build dumb little "state machines" with Python decorators.

### Installation

To install decorstate from [pip](https://pypi.python.org/pypi/pip):
```bash
$ pip install decorstate
```

To install decorstate from source:
```bash
$ git clone git@github.com:ahawker/decorstate.git
$ cd decorstate
$ python setup.py install
```

### Usage

How do I use this pile?

```python
import decorstate

class Switch(object):
state = 'off'

@decorstate.transition('off', 'on')
def on(self, *args, **kwargs):
print 'You turned me on!'

@decorstate.transition('on', 'off')
def off(self, *args, **kwargs):
print 'You turned me off!'

>>> switch = Switch()
>>> switch.state
'off'
>>> switch.on()
You turned me on!
'on'
>>> switch.off()
You turned me off!
'off'
```

A switch? Really? How lame.


```python
import decorstate

class BrokenSwitch(object):
state = 'off'

@decorstate.transition('off', 'on')
def on(self, *args, **kwargs):
print 'You turned me on!'

@decorstate.transition('on', 'off')
def off(self, *args, **kwargs):
print 'You turned me off? Nah!'

@off.guard
def off(self, *args, **kwargs):
print 'Ha! I laugh at your feeble attempt!'

>>> broken_switch = BrokenSwitch()
>>> broken_switch.state
'off'
>>> broken_switch.on()
You turned me on!
'on'
>>> broken_switch.off()
Ha! I laugh at your feeble attempt!
'on'
>>> broken_switch.state
'on'
>>> broken_switch.off()
Ha! I laugh at your feeble attempt!
'on'
>>> broken_switch.state
'on'
```

A broken switch? Yawn.


```python
import decorstate

class InstantOffSwitch(object):
state = 'off'

@decorstate.transition('off', 'off')
def on(self, *args, **kwargs):
print 'You turned me on!'

@decorstate.transition('on', 'off')
def off(self, *args, **kwargs):
print 'You turned me off!'

@on.after
def on(self, *args, **kwargs):
print 'Ha! No light for you!'

>>> instant_off_switch = InstantOffSwitch()
>>> instant_off_switch.state
'off'
>>> instant_off_switch.on()
You turned me on!
Ha! No light for you!
'off'
>>> instant_off_switch.state
'off'
```

Well, that's kinda mean.


```python
import decorstate
import random

class IoTSwitch(object):
state = 'off'

@decorstate.transition('off', 'off')
def on(self, *args, **kwargs):
print 'You turned me on? Maybe...'

@decorstate.transition('on', 'off')
def off(self, *args, **kwargs):
print 'You turned me off? Maybe...'

@on.guard
def on(self, *args, **kwargs):
return self.coin_flip()

@off.guard
def off(self, *args, **kwargs):
return not self.coin_flip()

@staticmethod
def coin_flip():
return random.randint(1, 2) == 1

>>> iot_switch = IoTSwitch()
>>> iot_switch.state
'off'
>>> iot_switch.on()
'off'
>>> iot_switch.on()
'off'
>>> iot_switch.on()
'off'
>>> iot_switch.on()
You turned me on? Maybe...
'on'
>>> iot_switch.off()
'on'
>>> iot_switch.off()
'on'
>>> iot_switch.off()
'on'
>>> iot_switch.off()
'on'
>>> iot_switch.off()
You turned me off? Maybe...
'off'
```

Hey now, why you hating? Internet powered light switches are next level shit. My living room has its own twitter feed.


### Why?

I was interesting in doing something a bit more complex using the Python [descriptor protocol](https://docs.python.org/2/howto/descriptor.html).

### TODO?

Random thoughts and musing about potential changes/features.

* Consider adding the @machine decorator back as currently, you cannot use the "state", "transition" and "transition_event" attributes until the first transition has been performed since they are lazy created.
* Add event handler that fires only when "entering" a state and not when you perform multiple transitions but stay in the same state.

### License

[Apache 2.0](LICENSE)

Metadata

Release files for decorstate 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for decorstate 0.0.3
File Size Uploaded Provenance
decorstate-0.0.3.tar.gz 5.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for decorstate 0.0.3
File Interpreter ABI Platform
decorstate-0.0.3-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 17.5 kB

Release files / decorstate-0.0.3.tar.gz

Download URL decorstate-0.0.3.tar.gz
Size 5.7 kB
Tags Source
SHA-256 checksum
How to use checksums
285fe86bafd8f99a021926c5083c893e586ba81ba8380d960001d4ec61ed88ba
BLAKE2b-256 checksum
How to use checksums
67307ca32fb3b6474e344dc33b53c9a07034514cea1c286c88e4386cc8175194
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release files / decorstate-0.0.3-py2.py3-none-any.whl

Download URL decorstate-0.0.3-py2.py3-none-any.whl
Size 11.8 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
2b10af7eb77c14cb863786355e11ebe2c6e4030407bd9fb0b8c5ea2747df1e0e
BLAKE2b-256 checksum
How to use checksums
62c562632f1de0c317911fb5e5d32a0f1c5b3b049f6fbc2cf20ebd332e60bac7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Security for decorstate 0.0.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.3 This release

2 release files No provenance

0.0.2

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page