Skip to main content

Mr. Bent knows his numbers.

Project description

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]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mr.bent-1.0a1.zip (22.6 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page