Package: parameterize
parameterize============*a Python implementation of [SRFI 39][]*[SRFI 39]: http://srfi.schemers.org/srfi-39/srfi-39.html[![Build Status](https://travis-ci.org/agrif/parameterize.svg)](https://travis-ci.org/agrif/parameterize)*parameterize* implements optional dynamic scope and parameter objectsin Python. It's a spiritual implementation of parameters from Scheme,as outlined in [SRFI 39][]. If you know what that means, awesome! Ifnot, read on.### Requirements*parameterize* should work, by itself, on Python 2.7 or later(including Python 3).Parameter Objects-----------------Basically, parameter objects hold a single value. They are meant to bedeclared as global variables, so that the values they contain can beaccessed anywhere. However, parameter objects have a neat trick: theyhave a context manager, `parameterize()`, that lets you override theparameter's value for a single piece of code. Any changes to theparameter that happen within this code block can't escape it, andcan't affect other threads of execution.So, parameters are sort of halfway between global variables and localvariables. They're accessable from anywhere, and modifiable fromanywhere, but modifications are always contained completely withintheir `parameterize()` block.Motivating Example------------------In Python, the `sys.stdout` global variable can be overridden to pointto any file-like object to redirect output from built-in outputprimitives like `print()`. So, there's a nice hacky way to redirectthe output of a function:~~~~{.py}with open('output.txt', 'w') as f:oldstdout = sys.stdoutsys.stdout = fsomething_that_prints_a_lot()sys.stdout = oldstdout~~~~This is definitely a hack, though. It can be made a little bit nicerby wrapping the replacement code into a context manager, but it's hardto write a context manager that works for any variable like this (whatif we wanted to redirect `sys.stderr` instead?). Also, this has issueswith threading: if another thread tries to write to `stdout` while`something_that_prints_a_lot()` is running, it'll end up in`output.txt`.*parameterize* and Parameter objects solve all these little problemsfor you. All we need to do is create an `stdout` parameter (with theinitial value of `sys.stdout`), and replace `sys.stdout` with a proxyobject that always points to the value of this parameter:~~~~{.py}import parameterizeimport sys# create our parameter and proxy objectstdoutp = parameterize.Parameter(sys.stdout)sys.stdout = stdoutp.proxy()print('before')with open('output.txt', 'w') as f:with stdoutp.parameterize(f):print('inside')print('after')~~~~This code will print "before" and "after" to the console, and "inside"to `output.txt`. The mutation of `sys.stdout` only appears within the`with stdoutp.parameterize` block, and plays nicely with otherthreads.This sort of pattern (where there's some global context that affectspossibly deeply-nested functions, without passing this context throughthe call chain) is also very common in web frameworks, where codehandling a response needs access to the request data, but may not havebeen passed it directly. Werkzeug calls them[context locals][werkzeug], and Flask calls it an[application context][flask]. *parameterize* is a stand-alone way todo this outside of these frameworks.[werkzeug]: http://werkzeug.pocoo.org/docs/local/[flask]: http://flask.pocoo.org/docs/appcontext/#app-contextIncidentally, the above example about replacing `sys.stdout` with aparameter is exactly how R7RS Scheme handles IO redirection.API---*parameterize* is a small module. To create a parameter object, use`Parameter(default, converter)`. `default` sets the initial value,and defaults to `None` when not provided. `converter` is a functionthat is called whenever the parameter is set, and should return avalidated or converted value. If `converter` is not supplied, itdefaults to doing nothing.Parameter objects have a `get()` method, and a `set(v)` method, to getand set the value contained by the object. As a convenience, if youhave a parameter object `p`, `p()` is the same as `p.get()`, and`p(v)` is the same as `p.set(v)`.Parameter objects also have `parameterize(v)`, a context manager thatsets the parameter to `v` within its block, and prevents all changesto the parameter within that block from escaping. This is *the bigdeal* about *parameterize*, and what seperates parameters from normalglobal variables.If the parameter value is an object, you can call `proxy()` to get anobject that will act like the object contained within the parameter,and track any changes made to that value. This proxying mechanism is awork in progress, and is probably more magic than you need. Still, itis useful sometimes. Patches *very* welcome.License-------*parameterize* is distributed under the MIT License. See `LICENSE.txt`for details.