Source

options / README.rst

Diff from to

File README.rst

 A module that helps encapsulate option and configuration data using a
 multi-layer stacking (a.k.a. nested context) model.
 
-Classes, for example, set default values for all
-instances. Instances can set new values. If an instance doesn't set a value, the
-class-set default "shines through" and remains in effect. Individual method
-calls can also set transient values that apply just for that call. If the call
-doesn't set a value, the instance value applies. If the instance didn't set a
-value, the class default applies.
+Classes are expected to define default option values. When instances are
+created, they can be instantiated with "override" values. For any option that
+the instances doesn't override, the class default "shines through" and remains
+in effect. Similarly, individual method calls can set transient values that
+apply just for the duration of that call. If the call doesn't set a value, the
+instance value applies. If the instance didn't set a
+value, the class default applies. Python's ``with`` statement can be used to
+tweak options for essentially arbitrary duration.
 
-This layered or stacked approach is particularly helpful when you have highly
+This layered or stacked approach is particularly helpful for highly
 functional classes that aim for "reasonable" or "intelligent" defaults and
-behaviors, yet that allow users to override those defaults at any time, and that
-aim for a simple, unobtrusive API.
+behaviors, that allow users to override those defaults at any time, and that
+aim for a simple, unobtrusive API. It can also be used to provide flexible
+option handling for functions.
 
-This 
-option-handling pattern is based on delegation rather than inheritance. It's
-described in `this StackOverflow.com discussion of "configuration sprawl" 
+This option-handling pattern is based on delegation rather than inheritance.
+It's described in `this StackOverflow.com discussion of "configuration sprawl" 
 <http://stackoverflow.com/questions/11702437/where-to-keep-options-values-paths-to-important-files-etc/11703813#11703813>`_.
 
 Unfortunately, it's a bit hard to demonstrate the virtues of this approach with
-simple code. Python already has pretty flexible function arguments, inlcuding
-variable number of arguments (``*args``), keyword arguments, and optional
-keyword arguments (``**kwargs``). Combined with object inheritance, base Python
-already covers a large number of use cases. But when you have a large number of
-configuration and instance variables, and when you might want to temporarily
-override either class or instance settings, things get dicey. This messy,
-complicated space is where ``options`` truly begins to shine.
+simple code. Python already supports flexible function arguments, including
+variable number of arguments (``*args``) and optional keyword arguments
+(``**kwargs``). Combined with object inheritance, base Python features already
+cover a large number of use cases and requirements. But when you have a large
+number of configuration and instance variables, and when you might want to
+temporarily override either class or instance settings, things get dicey. This
+messy, complicated space is where ``options`` truly begins to shine.
 
 Usage
 =====
 One problem here is that we broke apart the values provided to ``__init__()`` into
 separate instance variables, now we need to re-assemble them into something unified.
 And we need to explicitly choose between the ``**kwargs`` and the instance variables.
-This is pretty repetitive, and not very pretty. Another classic alternative, using
-native keyword arguments, is not much
+It gets repetitive, and is not pretty. Another classic alternative, using
+native keyword arguments, is no
 better::
 
         def draw2(self, name=None, color=None, height=None, width=None):
             width  = width  or self.width
             print "color='{}', width={}, name='{}', height={}".format(color, width, name, height)
 
-If we add just a few more instance variables, we have the `Mr. Creosote <http://en.wikipedia.org/wiki/Mr_Creosote>`_
-of class design on our hands. Not good. Things get worse if we want to set
-default values for all shapes in the class. We have to rework every method that
-uses values, the ``__init__`` method, *et cetera*. We've entered
-"just one more wafer-thin
-mint..." territory.
+If we add just a few more instance variables, we have the `Mr. Creosote
+<http://en.wikipedia.org/wiki/Mr_Creosote>`_ of class design on our hands. Not
+good. Things get worse if we want to set default values for all shapes in the
+class. We have to rework every method that uses values, the ``__init__`` method,
+*et cetera*. We've entered "just one more wafer-thin mint..." territory.
 
 But with ``options``, it's easy::
 
 instance variable approach becomes, and the more desirable the delegation
 alternative. Inheritance is a great software pattern for many kinds of data and
 program structures, but it's a bad pattern for complex option and configuration
-handling. For richly featured classes, the delegation pattern used by
-``options`` is much simpler. Even a large number of options requires almost no
-additional code and imposes no additional complexity or failure modes. By consolidating
-options into one place, and by allowing neat attribute-style access, everything is
-kept tidy. We can add new options or methods with confidence::
+handling. For richly featured classes, the delegation pattern ``options`` proves
+simpler. Supporting even a large number of options requires almost no additional
+code and imposes no additional complexity or failure modes. By consolidating
+options into one place, and by allowing neat, attribute-style access, everything
+is kept tidy. We can add new options or methods with confidence::
 
     def is_tall(self, **kwargs):
         opts = self.options.push(kwargs)
         return opts.height > 100
 
-Under the covers, ``options`` uses a variation on the ``ChainMap``
-data structure
-(a multi-layer dictionary) to provide its option stacking. Every option set is
-stacked on top of previously set option sets, with lower-level values shinging
-through if they're not set at higher levels. This stacking or overlay model
-resmebles how local and global variables are managed in many programming
-languages.
+Under the covers, ``options`` uses a variation on the ``ChainMap`` data
+structure (a multi-layer dictionary) to provide its option stacking. Every
+option set is stacked on top of previously set option sets, with lower-level
+values shining through if they're not set at higher levels. This stacking or
+overlay model resembles how local and global variables are managed in many
+programming languages.
 
 Magic Parameters
 ================
 
 Python's ``*args`` variable-number of arguments and ``**kwargs`` keyword
-arguments are sometimes called "magic" arguments. ``options`` takes this up
-a notch, allowing setters much like Python's ``property`` function or ``@property``
-decorator, in turn allowing arguments to be interpreted on the fly. This is useful, for instance,
-to provide relative rather than just absolute values. As an example, say that
-we added this code after
-``Shape.options``
-was defined::
+arguments are sometimes called "magic" arguments. ``options`` takes this up a
+notch, allowing setters much like Python's ``property`` function or
+``@property`` decorator. This allows arguments to be interpreted on the fly.
+This is useful, for instance, to provide relative rather than just absolute
+values. As an example, say that we added this code after ``Shape.options`` was
+defined::
 
     options.magic(
         height = lambda v, cur: cur.height + int(v) if isinstance(v, str) else v,
         width  = lambda v, cur: cur.width  + int(v) if isinstance(v, str) else v
     )
     
-Now, in addition to absolute ``height`` and ``width`` parameters which are provided
-by specifying those values as ``int``, your module
-auto-magically supports relative parameters.::
+Now, in addition to absolute ``height`` and ``width`` parameters which are
+provided by specifying ``int`` (integer/numeric) values, your module
+auto-magically supports relative parameters for ``height`` and ``width``.::
 
     one.draw(width='+200')
     
     color='blue', width=40, name='one', height=5
     
 Magically interpreted parameters are the sort of thing that one doesn't need
-very often or for every parameter--but when 
-it's useful, it's *enormously* useful and highly leveraged, leading
-to much simpler, much higher function
-APIs. We call them 'magical' here because of the "auto-magical" interpretation,
-but they are really just analogs of Python object properties. The magic function
-is basically a "setter" for a dictionary element.
+very often or for every parameter--but when they're useful, they're *enormously*
+useful and highly leveraged, leading to much simpler, much higher function APIs.
+We call them 'magical' here because of the "auto-magical" interpretation, but
+they are really just analogs of Python object properties. The magic function is
+basically a "setter" for a dictionary element.
 
 Design Considerations
 =====================
 
-In general, classes will define a set of methods that are "outwards facing"--methods 
-called by external code when consuming the class's functionality.
-Those methods should generally expose their options through ``**kwargs``,
-creating a local variable (say ``opts``) that represents the sum of all options
-in use--the full stack of call, instance, and class options, including
-any present magical interpretations.
+In general, classes will define a set of methods that are "outwards
+facing"--methods called by external code when consuming the class's
+functionality. Those methods should generally expose their options through
+``**kwargs``, creating a local variable (say ``opts``) that represents the sum
+of all options in use--the full stack of call, instance, and class options,
+including any present magical interpretations.
 
 Internal class methods--the sort that are not generally called by external code,
 and that by Python convention are often prefixed by an underscore (``_``)--these
 function calls.
 
 ``options`` has broad utility, but it's not for every class or module. It best
-suits high-level front-end APIs that multiplex lots of potential functionality, and
-wish/need to do it in a clean/simple way. Classes for which the set of instance
-variables is small, or methods for which the set of known/possible parameters is
-limited--these work just fine with classic Python calling conventions. "Horses
-for courses."
+suits high-level front-end APIs that multiplex lots of potential functionality,
+and wish/need to do it in a clean/simple way. Classes for which the set of
+instance variables is small, or functions/methods for which the set of
+known/possible parameters is limited--these work just fine with classic Python
+calling conventions. For those, ``options`` is overkill. "Horses for courses."
 
 Setting and Unsetting
 =====================
 make sense. Your object might be a very high level entry point, for example,
 representing very large buckets of functionality, with many options. Some of
 those options are relevant to the current instance, while others are intended as
-pass-throughs for lower-level modules. This may seem a rarified case--and it is,
-relatively speaking. But it does happen, and when you need that kind of
-multi-level processing, it's really, really super amazingly handy to have.
+pass-throughs for lower-level modules/objects. This may seem a doubly rarefied
+case--and it is, relatively speaking. But it does happen, and when you need
+multi-level processing, it's really, really super amazingly handy to
+have it.
 
 ``options`` supports this in its core ``push()`` method by taking the values
 that are known to be part of the class's options, and deleting those from
     )
     
 Because some of the "additions" can be prohibitions (i.e. removing
-particular options from being set or used), this is"adding to" the superclass's
+particular options from being set or used), this is "adding to" the superclass's
 options in the sense of "adding a layer onto" rather than strict "adding
 options."
 
     
     def __init__(self, *args, **kwargs):
         self.options = Quoter.options.push(kwargs)
-        if args:
-            self.options.addflat(args, ['prefix', 'suffix'])
+        self.options.addflat(args, ['prefix', 'suffix'])
         
 to consume optional ``prefix`` and ``suffix`` flat arguments. This makes the following
 equivalent::
     q1 = Quoter('[', ']')
     q2 = Quoter(prefix='[', suffix=']')
 
-As a design
-decision, an explicit ``addflat()`` method is provided not as much for Zen of
-Python reasons ("Explicit is better than implicit."), but because flat arguments
-are commonly combined with abbreviation/shorthand conventions, which may require
+An explicit ``addflat()`` method is provided not as much for Zen of Python
+reasons ("Explicit is better than implicit."), but because flat arguments are
+commonly combined with abbreviation/shorthand conventions, which may require
 some logic to implement. For example, if only a ``prefix`` is given as a flat
-argument, you meay want to use the same value to implicitly set the ``suffix``.
+argument, you may want to use the same value to implicitly set the ``suffix``.
 To this end, addflat returns the set of keys that it consumed::
 
         if args:
 Notes
 =====
 
- * This is a work in progress. The underlying techniques have
-   been successfully used in multiple projects, but it remains in an evolving
-   state as a standalone module. The API may change over time.
-   Swim at your own risk.
+ * This is a work in progress. The underlying techniques have been successfully
+   used in multiple projects, but it remains in an evolving state as a
+   standalone module. The API may change over time. Swim at your own risk.
    
  * Open question: Could "magic" parameter processing be
    improved with a properties-based approach akin to that of `basicproperty <http://pypi.python.org/pypi/basicproperty>`_,
    The underlying ``stuf`` module and ``orderedstuf`` class is not
    certified for PyPy, and it exhibits a bug with file objects on PyPy.
    ``options`` works around this bug, and tests fine on PyPy. Still, 
-   support for PyPy should be considered experimental. 
+   buyer beware. 
    
  * Versions subsequent to 0.200 require a late-model version of ``stuf`` to
    avoid a problem its earlier iterations had with file objects. Versions after 0.320