Cleaning out the inbox: Contracts, Typecheck and Lockfiles.

by jesse in ,


Time to scrub some of the fifty+ things I've had flagged for followup from the past few weeks now that I can finally crawl out from under a rock. I watch the cheese shop RSS feed and the ANN: emails on the python-list quite a bit. Here's are some of the more interesting highlights I've found. First up: pycontract.

Programming by Contract allows a programmer to document a function/class with statements describing behavior.

This is interesting in that it uses the same-style trick epydoc does to an extend and adds syntax into the doc string - in the case of epydoc, it's all for doc-generation.

In the case of pycontract, it's actually parsed code. To quote the code:

# Implementation
# --------------
# This module is divided into four parts:
#
# 1 -- Find docstrings and parse them for contract expressions...
#
#      This is accomplished by:
#      a. recursive enumerating elements in modules, classes using code
#         lifted from the 'inspect' Python standard module
#         see: checkmod, _check
#      b. scanning the docstrings of public functions with a regular
#         expression looking for lines that matches 'pre:' 'post:' or
#         'inv:' at the start
#         see: parse_docstring
#      c. Using the 'tokenize' Python tokenizer to build expressions
#         see: _read_block, _read_decl, _read_token
#
# 2 -- Construct functions that do contract checking...
#      This is done by just constructing big strings that are function
#      definitions.  Each function or method gets associated with four
#      'helper' functions... to check pre-conditions, to save old
#      values, to check post-conditions, and of course the saved
#      original code.  These are stored as function attributes.
#         see: _install_wrapper, _define_checker
#
# 3 -- Run-time support for call checking
#         see: call_public_function_*, call_private_function_*,
#              call_public_method_*, call_private_method_*,
#              call_constructor_*, call_destructor_*
#
# 4 -- Declarations for use within contract expressions
#      includes 'forall' 'exists' 'implies' and exception classes

Note that if you look at the example sort.py script it does not exclude the possibility of doctests, in fact it uses them (I'm still not a huge face of doctest - but that's a different thing entirely).

Obviously, the first thing that comes to mind when looking at this (at least for me) is the ability for enforce argument types and other return values/etc which is nice - I just can't help but think that this could be done with decorator syntax instead ala the typecheck's module syntax:

@typecheck_args( (int, str) )

	Takes a two-tuple composed of an integer and a string

@typecheck_args( [int, int, str] )

	Takes a list, with types in a repeating pattern of integer, integer,
	string.
	The list must complete the pattern; for example, two integers would be
	invalid. For example, [3, 4, 's', 5, 6, 'f'] matches, but [3, 4] and
	[3, 4, 'f', 5, 6] do not because they do not complete the pattern.

For more on decorator syntax, you should read the PythonDecoratorLibrary wiki page!

Next up, is lock_file. Now, I think this is one of those things that everyone has had to do sooner or later. I know that of at least 4 projects I've worked on I've had to do something like this. This python module is one simple lock_file.py module and implements a nice, clean interface to this common problem.

I am a huge fan of this:

    from lock_file import LockFile

    lock_f = LockFile('/var/run/app.lock', wait = True)
    try:
        do_something_useful()
    finally:
        lock_f.release()

Obviously, if you wanted to get cute, you could also implement this in a decorator ala:

@acquire_lock
def main():
    ... stuff ...

And you could collapse it all nicely. I'm just a fan of this module based on how simple and clean it is. I've had to spend an inordinate amount of time in the /var/run/... tree and coordinating /var/lock/subsys/... lock files for shell scripts/init scripts, and I just want to hug the lock_file module. I'd love the inclusion of something exactly like this in the stdlib, but I know why that would/should not be.