[TIP] Is it kosher to subclass plugins?

Fernando Perez fperez.net at gmail.com
Tue Jun 24 14:17:37 PDT 2008


On Tue, Jun 24, 2008 at 7:13 AM, jason pellerin <jpellerin at gmail.com> wrote:

>> OK, thanks for your explanation.  Should I then understand that
>> plugins are never meant to be reused, and just copy the entire thing
>> wholesale, rename it and start from there?
>
> That won't help either, unless you rename all of the options. The
> thing to do if you want to reuse some behavior is subclass. The option
> handling is really the only difficult part, and the solution to that
> is to first, make sure you aren't registering the same options by
> registering only whatever options are unique to your subclass and
> second, piggyback off of the superclass by consuming its options,
> where relevant, in your configure() method. That is, if you want to
> use the --doctest-tests option in your subclass, you should *not*
> register it in options(), but you *should* reference it in configure()
> (that is, look in the options object passed to configure for whatever
> that option sets) to set your configuration or change your plugin's
> behavior.

OK, many thanks, that little tap-dance was the part I had originally missed.

BTW, is this documented in any FM, so I could have avoided hassling
you?  I tried reading, but besides these two resources (which were
useful but obviously not sufficient):

http://ivory.idyll.org/articles/nose-intro.html#writing-plug-ins-a-simple-guide
http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins

I didn't find much.

>> And there is no mechanism for customizing how the plugin
>> behaves, since it internally hardcodes the finder, doctestcase proxy,
>> etc (much like doctest itself hardcodes everything).
>
> Definitely a design flaw. I'm open to suggestions for opening it up
> and otherwise making what you're trying to do easier, if you have any.

I'm only sketching things out, so take this with a grain of salt.  But
perhaps a callback approach could work, with a simple option at the
command line for a file to load with the requisite callbacks:

nosetests --with-myplugin --myplugin-config=/path/to/pconfig.py ...

Where this special pconfig file would look like:

configopt1 = object()

def configopt2(blah,blah)..

etc.

The plugin would then perform an execfile() on that file and inspect
the resulting dictionary, which would be used as keywords for the
signature of a post-init method:

class myplugin():
 def configure():
  ns = {}
  execfile(configfile,ns)

  # extract from the user's execution namespace the flags for post-config:
  post_config = {}
  for kw in list_of_post_config_argnames:
     post_config[kw] = ns.get(kw)
  self.post_configure(**post_config)


This has requires  a tiny bit of duplication in that you have to
declare the list_of_post_config_argnames by hand, but if you really
want you can self-extract it using inspect.getargspec().  It also
assumes that all keywords have None as their default value, which for
this type of use is probably an OK convention.

This approach gives you a simple mechanism for users to extend plugins
with their own code, and for plugins to declare how to use that API
from the command line.  By allowing the user to declare arbitrary
callbacks in the namespace of the file, they can go from providing
custom classes (say a specialized DocTestFinder) to entire functions
that will get called by your code.

This is just a quick sketch, I'm sure someone on this list can easily
cook up something cleaner/smarter.  But this seems like a good use
case for a bit of callbacks.

Cheers,

f



More information about the testing-in-python mailing list