skip to navigation
skip to content

mr.bent 1.0a1

Mr. Bent knows his numbers.

A fussy little man in impeccable black jacket and pinstripe trousers.

Introduction

Mr Bent is a framework for allowing profile data to be collected in a Python application and viewed at different logical levels. The three concepts involved are a plugin, a piece of code that profiles an application, a context, a logical block of code for which you want reporting data, and a filter, a way of getting fine-grained information on where the results for a context came from.

Plugins

Plugins are callables that are given to the '''mkwrapper''' function which applies it to a function in your application.

This looks like:

mr.bent.wrapper.mkwrapper(foo.bar, plugincallable, "myplugin")

Which will cause '''plugincallable''' to be called on every invocation of '''foo.bar''' and add the results of the plugin to the current context as '''myplugin'''.

Plugins can return either a number or an iterable. If it returns an iterable it must contain either strings or numbers. The case of returning a number is considered equivalent to returning an iterable of length 1 of numbers.

Contexts

A context stores data generated by plugins. At any point a new context can be started which will be a "sub-context" of the currently active context. If there is no currently active context a new top-level one will be created.

Contexts are named with the dotted name of the function that they are created around, and return their data to a callback.

This looks like:

def mycallback(context, result, stats):
    return "%s <!-- %s -->" % (result, `stats`)

mr.bent.wrapper.mkcontext(bar.foo, mycallback)

This example would cause invocations of bar.foo, a function that returns XML, to return the XML with a repr of the context dict in a following comment.

When a context ends it returns a mapping of the data it collected. As contexts are nested each time parent contexts include the data of their sub-contexts. Hence, the top level context returns the overall profiling; there is no need to manually aggregate data.

Filters

A filter is, like most things in Mr. Bent, a wrapper around a function. This will default to the dotted name of the callable, but an alternative, application specific name can be used instead. This is especially useful for a function that is used to render multiple different logical blocks of content.

This looks like:

mr.bent.wrapper.mkfilter(take.me.to.the.foo.bar)

Concrete example

In this example we have an application that renders a page of HTML including fragments that are logically different files which are then included into the main page.

Example 1:

.-------------.
|  Top level  |
`-------------'
       |
       |         .--------------------.
       |---------|  Left hand column  |
       |         `--------------------'
       |                   |
       |                   |              .-------------.
       |                   |--------------|  Login box  |
       |                   |              `-------------'
       |                   |
       |                   |
       |                   |              .------------------.
       |                   `--------------|  Navigation box  |
       |                                  `------------------'
       |
       |         .-----------------.
       |---------|  Content block  |
       |         `-----------------'
       |
       |
       |         .---------------------.
       `---------|  Right hand column  |
                 `---------------------'
                           |
                           |              .----------------.
                           `--------------|  Calendar box  |
                                          `----------------'

In this system we have the following notional plugins (with short names for brevity):

t:A timing plugin This plugin returns the number of milliseconds between it being invoked and it being stopped
d:A database access counting plugin This plugin returns how many times data was retrieved from a database.

The return values may look something like this:

{'t': [5, 15, 85, 25], 'd': [0, 1, 2, 8]}
.-------------.
|  Top level  |
`-------------'
       |         {'t': [5, 15], 'd': [0,1]}
       |         .--------------------.
       |---------|  Left hand column  |
       |         `--------------------'
       |                   |              {'t': [5], 'd': [0]}
       |                   |              .-------------.
       |                   |--------------|  Login box  |
       |                   |              `-------------'
       |                   |
       |                   |              {'t': [15], 'd': [1]}
       |                   |              .------------------.
       |                   `--------------|  Navigation box  |
       |                                  `------------------'
       |         {'t': [85], 'd': [2]}
       |         .-----------------.
       |---------|  Content block  |
       |         `-----------------'
       |
       |         {'t': [25], 'd': [8]}
       |         .---------------------.
       `---------|  Right hand column  |
                 `---------------------'
                           |              {'t': [25], 'd': [8]}
                           |              .----------------.
                           `--------------|  Calendar box  |
                                          `----------------'

Hence, the user has data at each level he has defined which he can then process as he likes.

Lets see that again as a doctest (sorry Florian!):

>>> from mr.bent.mavolio import create, destroy, current
>>> create("top")           # Create the top level context


>>> create("lefthand")      # Create the left hand column
>>> create("login")         # and the login portlet
>>> current()               # show that it's an empty context
{}
>>> current()['t'] = [5]    # Simulate plugin results being added to context
>>> current()['d'] = [0]
>>> destroy()               # Leave context
{'t': [5], 'd': [0]}
>>> create("nav")           # Create nav
>>> current()['t']=[15]
>>> current()['d']=[1]
>>> destroy()               # Leave nav
{'t': [15], 'd': [1]}
>>> destroy()               # Leave left hand column
{'t': [5, 15], 'd': [0, 1]}


>>> create("content")       # Enter content block
>>> current()['t'] = [85]
>>> current()['d'] = [2]
>>> destroy()               # Leave content block
{'t': [85], 'd': [2]}


>>> create("righthand")     # Enter right hand column
>>> create("cal")           # Enter calendar box
>>> current()['t']=[25]
>>> current()['d']=[8]
>>> destroy()               # Leave calendar
{'t': [25], 'd': [8]}
>>> destroy()               # Leave right hand column
{'t': [25], 'd': [8]}


>>> destroy()               # Leave the top level context, get totals
{'t': [5, 15, 85, 25], 'd': [0, 1, 2, 8]}

Method reference

Utility Methods

mr.bent.wrapper.mkwrapper(function, plugin, name):
 Wraps a function with a plugin which writes its data to the current context as name
mr.bent.wrapper.mkcontext(function, callback):
 Wraps a function to create a new context on invocation, and close it when it finishes, and give the data to callback to handle reporting.
mr.bent.wrapper.mkfilter(function):
 Wraps a function to be a key that context reporting data can be filtered on.

Low level methods

mr.bent.mavolio.create(name):
 Creates a new context called name.
mr.bent.mavolio.destroy():
 Ends the current context and returns the statistics.
mr.bent.mavolio.current():
 Returns the current, in progress, context dict.

Changelog

1.0a1 - Unreleased

  • Initial release [matthewwilkes, fschulze, witsch]
File Type Py Version Uploaded on Size # downloads
mr.bent-1.0a1.zip (md5, pgp) Source 2008-12-14 14:33:47 22KB 226

Log in to rate this package.