Contents¶
Overview¶
Implementation of the Common Lisp’s conditions system in Python.
Free software: BSD license.
docs | |
---|---|
tests | |
package |
Rationale¶
Common Lisp (CL) has a very rich condition system. Conditions in CL is a some sort of signals, and used not only for exception handling but also in some other patterns. There is a very good explanation of how they works – a chapter from the book Practical Common Lisp by Peter Seibel: Beyond Exception Handling: Conditions and Restarts.
Python’s exceptions cover only one scenerio from this book, but Common Lisp’s conditions allows more interesting usage, particlarly “restarts”. Restart is a way to continue code execution after the exception was signaled, without unwinding a call stack. I’ll repeat: without unwinding a call stack.
Moreover, conditions allows to the author of the library to define varios cases to be choosen to take over the exception.
Example¶
Here is example from the book, but implemented in python using conditions library:
def parse_log_entry(text):
"""This function does all real job on log line parsing.
it setup two cases for restart parsing if a line
with wrong format was found.
Restarts:
- use_value: just retuns an object it was passed. This can
be any value.
- reparse: calls `parse_log_entry` again with other text value.
Beware, this call can lead to infinite recursion.
"""
text = text.strip()
if well_formed_log_entry_p(text):
return LogEntry(text)
else:
def use_value(obj):
return obj
def reparse(text):
return parse_log_entry(text)
with restarts(use_value,
reparse) as call:
return call(signal, MalformedLogEntryError(text))
def log_analyzer(path):
"""This procedure replaces every line which can't be parsed
with special object MalformedLogEntry.
"""
with handle(MalformedLogEntryError,
lambda (c):
invoke_restart('use_value',
MalformedLogEntry(c.text))):
for filename in find_all_logs(path):
analyze_log(filename)
def log_analyzer2(path):
"""This procedure considers every line which can't be parsed
as a line with ERROR level.
"""
with handle(MalformedLogEntryError,
lambda (c):
invoke_restart('reparse',
'ERROR: ' + c.text)):
for filename in find_all_logs(path):
analyze_log(filename)
What we have here is a function parse_log_entry
which defines two
ways of handling an exceptional situation: use_value
and reparse
.
But decision how bad lines should be handled is made by high level function
log_analyser
. Original book’s chapter have only one version of the
log_analyser
, but I’ve added an alternative log_analyser2
to
illustrate a why restarts is a useful pattern. The value of this
pattern is in the ability to move dicision making code from low level
library functions into the higher level business logic.
Full version of this example can be found in example/example.py file.
Installation¶
pip install conditions
Documentation¶
Development¶
To run the all tests run:
tox
Note, to combine the coverage data from all the tox environments run:
Windows | set PYTEST_ADDOPTS=--cov-append
tox
|
---|---|
Other | PYTEST_ADDOPTS=--cov-append tox
|
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
Bug reports¶
When reporting a bug please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
Documentation improvements¶
cl-conditions could always use more documentation, whether as part of the official cl-conditions docs, in docstrings, or even on the web in blog posts, articles, and such.
Feature requests and feedback¶
The best way to send feedback is to file an issue at https://github.com/svetlyak40wt/python-cl-conditions/issues.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that code contributions are welcome :)
Development¶
To set up python-cl-conditions for local development:
Fork python-cl-conditions (look for the “Fork” button).
Clone your fork locally:
git clone git@github.com:your_name_here/python-cl-conditions.git
Create a branch for local development:
git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, run all the checks, doc builder and spell checker with tox one command:
tox
Commit your changes and push your branch to GitHub:
git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
If you need some code review or feedback while you’re developing the code just make the pull request.
For merging, you should:
- Include passing tests (run
tox
) [1]. - Update documentation when there’s new API, functionality etc.
- Add a note to
CHANGELOG.rst
about the changes. - Add yourself to
AUTHORS.rst
.
[1] | If you don’t have all the necessary python versions available locally you can rely on Travis - it will run the tests for each change you add in the pull request. It will be slower though ... |
Tips¶
To run a subset of tests:
tox -e envname -- py.test -k test_myfeature
To run all the test environments in parallel (you need to pip install detox
):
detox
Authors¶
- Alexander Artemenko - http://dev.svetlyak.ru