Skip to content

Instantly share code, notes, and snippets.

@code-machina
Forked from palankai/specification.py
Created November 26, 2019 12:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save code-machina/50285e362b3b8a8261d0be396ea5ec92 to your computer and use it in GitHub Desktop.
Save code-machina/50285e362b3b8a8261d0be396ea5ec92 to your computer and use it in GitHub Desktop.
Python Specification Pattern
class Specification:
def __and__(self, other):
return And(self, other)
def __or__(self, other):
return Or(self, other)
def __xor__(self, other):
return Xor(self, other)
def __invert__(self):
return Invert(self)
def is_satisfied_by(self, candidate):
raise NotImplementedError()
def remainder_unsatisfied_by(self, candidate):
if self.is_satisfied_by(candidate):
return None
else:
return self
class CompositeSpecification(Specification):
pass
class MultaryCompositeSpecification(CompositeSpecification):
def __init__(self, *specifications):
self.specifications = specifications
class And(MultaryCompositeSpecification):
def __and__(self, other):
if isinstance(other, And):
self.specifications += other.specifications
else:
self.specifications += (other, )
return self
def is_satisfied_by(self, candidate):
satisfied = all([
specification.is_satisfied_by(candidate)
for specification in self.specifications
])
return satisfied
def remainder_unsatisfied_by(self, candidate):
non_satisfied = [
specification
for specification in self.specifications
if not specification.is_satisfied_by(candidate)
]
if not non_satisfied:
return None
if len(non_satisfied) == 1:
return non_satisfied[0]
if len(non_satisfied) == len(self.specifications):
return self
return And(*non_satisfied)
class Or(MultaryCompositeSpecification):
def __or__(self, other):
if isinstance(other, Or):
self.specifications += other.specifications
else:
self.specifications += (other, )
return self
def is_satisfied_by(self, candidate):
satisfied = any([
specification.is_satisfied_by(candidate)
for specification in self.specifications
])
return satisfied
class UnaryCompositeSpecification(CompositeSpecification):
def __init__(self, specification):
self.specification = specification
class Invert(UnaryCompositeSpecification):
def is_satisfied_by(self, candidate):
return not self.specification.is_satisfied_by(candidate)
class BinaryCompositeSpecification(CompositeSpecification):
def __init__(self, left, right):
self.left = left
self.right = right
class Xor(BinaryCompositeSpecification):
def is_satisfied_by(self, candidate):
return (
self.left.is_satisfied_by(candidate) ^
self.right.is_satisfied_by(candidate)
)
class NullaryCompositeSpecification(CompositeSpecification):
pass
class TrueSpecification(NullaryCompositeSpecification):
def is_satisfied_by(self, candidate):
return True
class FalseSpecification(NullaryCompositeSpecification):
def is_satisfied_by(self, candidate):
return False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment