A simple python implementation of Specification pattern.
Project description
Sutoppu
Sutoppu (ストップ - Japanese from English Stop) is a simple python implementation of Specification pattern.
What is Specification Pattern?
See Wikipedia.
In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.
More information: Eric Evans and Martin Fowler article about Specifications
Basic usage
Installation
$ pip install sutoppu
Usage
from sutoppu import Specification
class Fruit:
def __init__(self, color: str, sweet: bool, bitter: bool):
self.color = color
self.sweet = sweet
self.bitter = bitter
class FruitIsBitter(Specification):
description = 'The given fruit must be bitter.'
def is_satisfied_by(self, fruit: Fruit):
return fruit.bitter is True
class FruitIsSweet(Specification):
description = 'The given fruit must be sweet.'
def is_satisfied_by(self, fruit: Fruit):
return fruit.sweet is True
class FruitIsYellow(Specification):
description = 'The given fruit must be yellow.'
def is_satisfied_by(self, fruit: Fruit):
return self.color == 'yellow'
>>> lemon = Fruit(color='yellow', sweet=False, bitter=True)
>>> is_a_lemon = FruitIsYellow() & FruitIsBitter() & ~FruitIsSweet()
>>> is_a_lemon.is_satisfied_by(lemon)
True
Operators
Bitwise operators are overload to provide simple syntax.
And: yellow
>>> my_spec = SpecificationA() & SpecificationB()
Or:
>>> my_spec = SpecificationA() | SpecificationB()
Not:
>>> my_spec = ~SpecificationA()
Lighter syntax
If you do not find the is_satisfied_by
method very convenient you can also directly call the specification as below.
>>> lemon = Fruit(color='yellow', sweet=False, bitter=True)
>>> is_a_lime = FruitIsGreen() & FruitIsBitter() & ~FruitIsSweet()
>>> is_a_lime(lemon)
False
Error reporting
It can be difficult to know which specification failed in a complex rule. Sutoppu allows to list all the failed specifications by getting the errors
attribute after use.
The errors
attribute is reset each time the specification is used. For each failed specification, it returns a dict with the name of the specification class for key and the description provide in the class for value. In the case where the specification failed with a not
condition, the description is prefixed with Not ~
.
>>> apple = Fruit(color='red', sweet=True, bitter=False)
>>> is_a_lemon = FruitIsYellow() & FruitIsBitter() & ~ FruitIsSweet()
>>> is_a_lemon.is_satisfied_by(apple)
False
>>> is_a_lemon.errors
{
'FruitIsColored': 'The given fruit must be yellow.',
'FruitIsBitter': 'The given fruit must be bitter.',
'FruitIsSweet': 'Not ~ The given fruit must be sweet.'
}
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.