
Using the Make_call_* Constructors
**********************************

To use the library constructors, add this import line:

   import dectools

Use a constructor as decorator on your function.  Your function must
have the correct function signature:  the first argument must be
"function" and the second and third arguments are probably "args" and
"kwargs".   Put any additional arguments after these required
arguments.

TODO:  Make these arguments optional, because only @make_call_instead
really needs them.

For example:

   @make_call_before
   def my_new_call_before_decorator(function, args, kwargs, some_other_argument):
        # do stuff
        ....

   @my_new_call_before_decorator("foo")  # or (some_other_argument = "foo")
   def my_newly_decorated_function(other_args):
       pass

We will call "my_new_call_before_decorator" the decorator and
"my_newly_decorated_function" the decorated function.


Quick Reference
===============

+---------------------+----------------------------+--------------------------------------------------------------+
| Construtor          | Required Parameters        | Used For                                                     |
+=====================+============================+==============================================================+
| @make_call_once     | function                   | registering with frameworks, global resource creation        |
+---------------------+----------------------------+--------------------------------------------------------------+
| @make_call_before   | funciton, args, kwargs     | requiring login, lazy instantiation                          |
+---------------------+----------------------------+--------------------------------------------------------------+
| @make_call_if       | function, args, kwargs     | checking priviledges, skipping if overloaded                 |
+---------------------+----------------------------+--------------------------------------------------------------+
| @make_call_after    | function, args, kwargs     | converting return values to exceptions, redrawing the screen |
+---------------------+----------------------------+--------------------------------------------------------------+
| @make_call_instead  | function, args, kwargs     | everything else.  logs, caches, locking, resource handling.  |
+---------------------+----------------------------+--------------------------------------------------------------+


@make_call_once
===============

   Requires:  ``(function, ...)``

   Call the decorator once when the decorator is applied, but does not
   affect the decorated function.

   Used for registering the function with frameworks, global resource
   creation, or enforcing proprietary licensing restrictions.


@make_call_before
=================

   Requires: ``(function, args, kwargs, ...)``

   Call the decorator before the decorated function, then call the
   decorated function. If the decorator throws an exception, then the
   decorated function is not called.

   Used for requiring users to be logged in first, lazy instantiation
   of resources, and setting preconditions.


@make_call_after
================

   Requires: ``(function, args, kwargs, ...)``

   Call decorator after the decorated function.  The decorator's
   return value is returned.

   Used for converting errors from return values into exceptions,
   double checking that resources were released, and redrawing of the
   screen.


@make_call_if
=============

   Requires: ``(function, args, kwargs, ...)``

   Call decorator first.  If decorator returns a True value, then call
   the decorated function.

   Used for checking authorization, skipping decluttering during peak
   times, and enforcing some proprietary licensing.


@make_call_instead
==================

   Requires: ``(function, args, kwargs, ...)``

   Call the decorator.  Do not call the decorated function.  The
   decorator will usually call the function with the statement
   ``return_value = function(*args, **kwargs)``.

   Used for everything, including logging, caching, acquiring and
   releasing locks, acquiring and releasing other resources, comparing
   results between an optimized algorithm and a slow but reliable one,
   and more.


How the Constructors Really Work
********************************

TODO:  Make the constructors really work this way instead of with a
couple extra levels of functino calls.  The concept is about right.

This follows through one example to show how it works.  Walking
through it might demystify decorators, might make you think I am an
idiot, and might put you to sleep.


The example
===========

   @make_call_instead   # Step 1
   def notice_me(function, args, kwargs, message = "I see you"):
       print message + ": " + function.__name__
       return function(args, kwargs)

   @notice_me("Watching you")  # Step 2, Parts A and B.
   def hello(name):
       print "Hello", name

   hello("Charles")  # Step 3.


The example (unwrapped)
=======================

This is the same example, except we do some extra assignements to
temporary or differently-named variables.  It also moves the ``@``
operation to after the ``def`` statement, which is where it actually
occurs.

   def notice_me(function, args, kwargs, message = "I see you"):
       print message + ": " + function.__name__
       return function(args, kwargs)
   widget = make_call_instead(notice_me)  # Step 1
   notice_me = widget


   def hello(name):
       print "Hello", name
   gizmo = widget("Watching you")  # Step 2 Part A
   hello_out = gizmo(hello)        # Step 2 Part B
   hello = hello_out

   hello("Charles")                # Step 3


The explanation
===============


Step 1
------

The constructor, @make_call_instead, is a concrete decorator because
it takes one function as input.  The function passed in, e.g.,
notice_me():

* has "function, args, and kwargs" as first three arguments.  In the
  current version, this is asserted.   In future versions, it might
  not be required.

* has zero or more additional arguments.  The additional arguments may
  or may not have default values.

The return value, named widget here, has some properties:

* It has a signature of only the additional parameters.  For this
  case, it has a signature of taking only the message argument.

* It has the __name__ metadata of notice_me().

* It has decorator data describing that "the function signature was
  changed" and mentions "make_call_instead()" as the culprit.

* It can be used in step 2.


Step 2, Part A
--------------

Step 2 is divided into two parts, because two separate operations
occur in the line @notice_me("Watching you").   notice_me("Watching
you") resolves into an intermediate value and the '@' operation is
then applied.

For Part A., gizmo = widget("Watching you") takes an argument.

* The argument corresponds to the additional arguments after
  "function, args, kwargs".

* The signature of widget is "def widget(message):" and
  widget("watching you") is equivalent to ``widget(message = "watching
  you")``.  Your IDE will help get the arguments correct.

* Widget is a complex decorator, taking arguments and yielding a
  decorator.

* dectools makes complex decorators out of functions.  "Function" is
  used interchangably with "method".

* The output, gizmo(), is a concrete decorator for use in Step 2, Part
  B.  gizmo() takes one argument, a callable function, and returns a
  callable function with the additional functionality of notice_me().
  This makes gizmo() a concrete decorator.

* When you use the decorator again, like *@notice_me("Still
  watching")*, or, equivalently, *gizmo_2 = widget("Still_watching")*
  both gizmo and gizmo_2 will have the same __code__ attribute but
  different __closure__ attributes.  If that made no sense, don't
  worry.  There will not be a quiz.  Just recognize that you will use
  notice_me with different input parameters.

* the output, gizmo, is used in Step 2, Part B.


Step 2, Part B
--------------

This line, "hello_out = gizmo(hello)" decorates hello.

* gizmo() takes exactly one argument, hello, and returns the modified
  function.  For the example, we call it hello_out for clarity.  Right
  after this line, hello_out is assigned to hello.

* hello_out has the same signature and ``__name__`` as hello.  These
  are not related to the signature or name of notice_me or gizmo.
  That is, using the decorator does not change the signature:  one of
  the magic points of dectools.

* hello is expected to be a function.  The library might support hello
  being a class in the future if I can make a case for it being
  helpful.  It works and is helpful for @make_call_once().

* hello_out does get additional metadata to record that it was
  decorated.


Step 3
------

When the line hello("charles") is called these things happen:

1. hello("charles") is called.  When it was decorated, the actual code
   for hello was changed to call a dectools function.  Let's call this
   function "call_through()" for this example.

2. "call_through()" then calls notice_me() with the original hello
   function and additonal decorator parameters.  In this case, it's
   called:

      notice_me(function= hello, args = ("charles"), kwargs = {}, message = "Watching you")

3. notice_me() then prints "Watching you: hello", then calls hello.  I
   wonder if anyone reads this documentation?  Email me if you do.

4. The return value of hello() is returned to notice_me() which is
   returned to call_through() which is returned to the main program,
   where it is ignored.
