I'm relatively new to Python from a strong Java background. I am trying to create a base class to do Hoare Logic. Function decorators seem to be the way to go with this. I've produced the following code, can you comment on this, and suggest an easier way to do this? It seems awfully over complicated to how easy this would be with an abstract function in Java. I'm quite liking Python's function points from my C/C++ days, something Java doesn't easily support without introspection.
Hoare Logic
{P} {C} {Q}
Where:
- P is the pre-condition
- C is the command
- Q is the post-condition or invariant.
The python decorator
def hoare_logic(description, pre_execution=None, post_execution=None):
def decorator_func(function_to_decorate):
def wrapper_func(*args, **kwargs):
# Hoare Pre-Condition
if pre_execution is not None:
pre_condition = pre_execution(*args, **kwargs)
print("Hoare Pre-Condition {!r}".format(description, pre_condition))
# Hoare Command
return_value = function_to_decorate(*args, **kwargs)
# Hoare Post-Condition
if post_execution is not None:
post_condition = post_execution(*args, **kwargs)
print("Hoare Post-Condition {!r}".format(description, post_condition))
return return_value
return wrapper_func
return decorator_func
Stubs for the pre & post-conditions
def pre_condition():
return True
def post_condition():
return True
Typical usage I want to perform
This is representative of the code under test in a specific scenario, with the pre & post conditions I'd expect to collect several of these into a BDD behaviour class extending the python unittest.TestCase
class, or perhaps my own extension of that.
@hoare_logic(description='description of command', pre_condition, post_condition)
def command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
These are just stubs for exercising the decorator usage above, this is not the way I will really expect to use this in practice, but it is so you can run this code.
# self.assertRaises(ValueError, command, None)
command(True)
command(False)
command('foo')
command('bar')
command('foobar')
Once I have a grip with the basic mechanism, I intend to adapt this to provide API contracts & a BDD type scenario functionality framework for behaviour & scenario testing. So please keep this in mind. I'm interested in serious scrutiny of the core functionality implementation of the Hoare logic in python.
I'm unsure of my treatment of *args, **kwargs
but from what I can see I need this for Python decorators. Is there a better way to handle parametrisation of the wrapped function?
I've deliberately exclude class wrapper & imports to make this simpler run & follow. The commented stub does work if this a class, but not as a script, not quite sure why.
Ultimately I want to be able to do the following, but didn't include this because it is not complete, so consider it pseudo code of my final objective.
@scenario(narrative='short description', givens=[given_one, given_two], thens=[then_one])
def the_behaviour_under_test(arg):
...
Please be kind, if I've broken the question rules, please help me improve the compliance of the question with comments rather than just close it.
Here is everything together Comments suggested I need to add more code, so the full file, this will run as a stand alone python script.
#!/usr/bin/env python
def hoare_logic(description, pre_execution=None, post_execution=None):
def decorator_func(function_to_decorate):
def wrapper_func(*args, **kwargs):
# Hoare Pre-Condition
if pre_execution is not None:
pre_condition = pre_execution()
print("Hoare Pre-Condition {!r}".format(description, pre_condition))
# Hoare Command
return_value = function_to_decorate(*args, **kwargs)
# Hoare Post-Condition
if post_execution is not None:
post_condition = post_execution()
print("Hoare Post-Condition {!r}".format(description, post_condition))
return return_value
return wrapper_func
return decorator_func
def pre_condition():
return True
def post_condition():
return True
@hoare_logic(description='description of command', pre_execution=pre_condition, post_execution=post_condition)
def command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
# command()
# self.assertRaises(ValueError, command, None)
command(True)
command(False)
command('foo')
command('bar')
command('foobar')
@hoare_logic(description='command')
def naked_command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
# naked_command()
# self.assertRaises(ValueError, naked_command, None)
naked_command(True)
naked_command(False)
naked_command('foo')
naked_command('bar')
naked_command('foobar')
@hoare_logic(description='description of command', pre_execution=pre_condition)
def pre_command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
# pre_command()
# self.assertRaises(ValueError, pre_command, None)
pre_command(True)
pre_command(False)
pre_command('foo')
pre_command('bar')
pre_command('foobar')
@hoare_logic(description='description of command', post_execution=post_condition)
def post_command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
# naked_command()
# self.assertRaises(ValueError, naked_command, None)
post_command(True)
post_command(False)
post_command('foo')
post_command('bar')
post_command('foobar')
@hoare_logic(description='description of command', pre_execution=pre_condition, post_execution=post_condition)
def post_command(arg):
if arg is not None:
print("command")
if arg == 'foobar':
return False
else:
return True
else:
raise ValueError
# post_command()
# self.assertRaises(ValueError, post_command, None)
post_command(True)
post_command(False)
post_command('foo')
post_command('bar')
post_command('foobar')