<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://community.activestate.com" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>ActiveState Community Site - The Impatient Developer&amp;#039;s Guide to Writing Python Nose Plugins - Comments</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins</link>
 <description>Comments for &quot;The Impatient Developer&#039;s Guide to Writing Python Nose Plugins&quot;</description>
 <language>en</language>
<item>
 <title>Ugh... there&#039;s something</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-6943</link>
 <description>&lt;p&gt;Ugh... there&#039;s something fishy going on here too with the above example and our plugin... investigating further.&lt;/p&gt;
</description>
 <pubDate>Mon, 03 Nov 2008 22:23:41 +0000</pubDate>
 <dc:creator>yaneurabeya</dc:creator>
 <guid isPermaLink="false">comment 6943 at http://community.activestate.com</guid>
</item>
<item>
 <title>Coding suggestion for formatErr with v0.10.3/0.10.4 incorrect</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-6932</link>
 <description>&lt;p&gt;Using the formatErr suggested above, you&#039;ll run into weird issues with package setup and teardown if you insert an error in __init__.py, etc, like I describe in:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://code.google.com/p/python-nose/issues/detail?id=217&quot; title=&quot;http://code.google.com/p/python-nose/issues/detail?id=217&quot;&gt;http://code.google.com/p/python-nose/issues/detail?id=217&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Following logic similar to the following should work better (this is from 0.10.3&#039;s nose.plugins.capture; FYI -- this is LGPL&#039;ed text):&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;def formatError(self, test, err):
        test.capturedOutput = output = self.buffer
        self._buf = None
        if not output:
            # Don&#039;t return None as that will prevent other
            # formatters from formatting and remove earlier formatters
            # formats, instead return the err we got
            return err
        ec, ev, tb = err
        return (ec, self.addCaptureToErr(ev, output), tb)&lt;/pre&gt;</description>
 <pubDate>Mon, 03 Nov 2008 03:21:10 +0000</pubDate>
 <dc:creator>yaneurabeya</dc:creator>
 <guid isPermaLink="false">comment 6932 at http://community.activestate.com</guid>
</item>
<item>
 <title>I&#039;ll post my attempt on the list too...</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-5248</link>
 <description>&lt;p&gt;... unless there&#039;s an outpouring of interest from Komodo community&lt;br /&gt;
members on writing Nose plug-ins, it makes total sense to move the&lt;br /&gt;
discussion there.&lt;/p&gt;
&lt;p&gt;For those interested, see &lt;a href=&quot;http://groups.google.com/gropu/nose-users?hl=en&quot; title=&quot;http://groups.google.com/gropu/nose-users?hl=en&quot;&gt;http://groups.google.com/gropu/nose-users?hl=en&lt;/a&gt;&lt;/p&gt;
</description>
 <pubDate>Tue, 29 Apr 2008 18:09:20 +0100</pubDate>
 <dc:creator>ericp</dc:creator>
 <guid isPermaLink="false">comment 5248 at http://community.activestate.com</guid>
</item>
<item>
 <title>Thanks Kumar, I will post</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-5240</link>
 <description>&lt;p&gt;Thanks Kumar, I will post there once I can refer to the code.&lt;/p&gt;
</description>
 <pubDate>Tue, 29 Apr 2008 15:01:48 +0100</pubDate>
 <dc:creator>bcorfman</dc:creator>
 <guid isPermaLink="false">comment 5240 at http://community.activestate.com</guid>
</item>
<item>
 <title>There might be a few more</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-5239</link>
 <description>&lt;p&gt;There might be a few more eyes on it if you post to the nose-users list&lt;/p&gt;
</description>
 <pubDate>Tue, 29 Apr 2008 13:51:46 +0100</pubDate>
 <dc:creator>kumar303</dc:creator>
 <guid isPermaLink="false">comment 5239 at http://community.activestate.com</guid>
</item>
<item>
 <title>Still in progress</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-5238</link>
 <description>&lt;p&gt;I created a new 1.2 branch for kNose so I could continue to experiment with using the nose API while still maintaining the 1.1.x version. I&#039;m at work right now and don&#039;t have access to the 1.2 code, but if you provide me a place to send it or upload it, I&#039;ll get you my code tomorrow. &lt;/p&gt;
&lt;p&gt;I have been completely successful with calling nose.run() from a Python shell, but only partially successful when calling it inside kNose. Inside the extension, nose will correctly return pass/fail/error on any test classes derived from unittest.TestCase, but it will always report success on standalone test functions, i.e.&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;def testMe():
    assert (1==0) # this should fail, but it doesn&#039;t&lt;/pre&gt;&lt;p&gt;
I figure it has something to do with how the environmental variables are set within nose, but right now I&#039;m at a loss, even when stepping through in the Komodo debugger. Some nose options cancel out others, and the documentation isn&#039;t much help in this regard. I have reported the behavior on the nose issues list (&lt;a href=&quot;http://code.google.com/p/python-nose/issues/list&quot; title=&quot;http://code.google.com/p/python-nose/issues/list&quot;&gt;http://code.google.com/p/python-nose/issues/list&lt;/a&gt;), but I haven&#039;t gotten a response yet.&lt;/p&gt;
&lt;p&gt;I got a recommendation from Kumar to call nose.main() using subprocess, instead of nose.run(). That&#039;s not much different than what I&#039;m doing in knose 1.1 though, and it&#039;s adding a lot of complexity just to call plugins. I&#039;d really rather get nose.run() working directly if there&#039;s any way to do so since that actually simplifies my code.&lt;/p&gt;
&lt;p&gt;That&#039;s the update. Let me know if you want to see what I have so far. I may be missing something obvious, and if you can suggest a fix, that would be great.&lt;/p&gt;
</description>
 <pubDate>Tue, 29 Apr 2008 13:34:05 +0100</pubDate>
 <dc:creator>bcorfman</dc:creator>
 <guid isPermaLink="false">comment 5238 at http://community.activestate.com</guid>
</item>
<item>
 <title>Is there more to running nose plugins without installing them?</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-5232</link>
 <description>&lt;p&gt;Hi Brandon,&lt;/p&gt;
&lt;p&gt;I&#039;ve revisited the Nose integration, working on making the tests extensible.&lt;br /&gt;
They work fine using the setup script, but I can&#039;t get them to work using&lt;br /&gt;
your technique a couple of comments back.  Is there more to it?  It seems&lt;br /&gt;
that using that technique, Nose won&#039;t load any tests, even when I pass&lt;br /&gt;
them as an explicit argv= in the call to nose.run&lt;/p&gt;
&lt;p&gt;Thanks,&lt;br /&gt;
Eric&lt;/p&gt;
</description>
 <pubDate>Mon, 28 Apr 2008 22:59:43 +0100</pubDate>
 <dc:creator>ericp</dc:creator>
 <guid isPermaLink="false">comment 5232 at http://community.activestate.com</guid>
</item>
<item>
 <title>I was almost finished with</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-4864</link>
 <description>&lt;p&gt;I was almost finished with updating kNose to use the plugin API, but I ran into a snag. It turns out that nose imports all unit test modules into memory and keeps them there if you use a nose.run() call. As a result, if you call nose.run() a second time, you will get stale information because the unit tests will not get reloaded into memory. &lt;/p&gt;
&lt;p&gt;You must use a &#039;--with-isolation&#039; flag in order to enable nose&#039;s IsolationPlugin; this makes nose unload the unit test modules after it completes each test. Unfortunately, this seems to work only with test classes derived from unittest.TestCase. If you have basic functions inside a module instead, these will still not get reloaded correctly.&lt;/p&gt;
&lt;p&gt;I&#039;ve got a couple of requests for help out to the TIP list and the nose issue tracker on Google Code. I will post an update here if I found out anything.&lt;/p&gt;
</description>
 <pubDate>Mon, 31 Mar 2008 20:22:32 +0100</pubDate>
 <dc:creator>bcorfman</dc:creator>
 <guid isPermaLink="false">comment 4864 at http://community.activestate.com</guid>
</item>
<item>
 <title>Eric, I&#039;ve been</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-4728</link>
 <description>&lt;p&gt;Eric, I&#039;ve been investigating your techniques in order to update kNose. You probably have found this out already, but your article here is referencing the nose 0.9 API, not the new 0.10 API. The new version of nose changes the plugin API (and the method signatures for your KomodoOutput class above). &lt;/p&gt;
&lt;p&gt;However, a very exciting thing about nose 0.10 is that it no longer requires plugins to be installed with a setup script. You can simply pass the plugin class to the nose.run command with a &#039;plugins&#039; keyword argument, like so:&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;import nose
class KomodoOutput(Plugin):
   ...

nose.run(plugins=[KomodoOutput()])&lt;/pre&gt;&lt;p&gt;
The new API is much more plugin friendly as a result.&lt;/p&gt;
</description>
 <pubDate>Thu, 20 Mar 2008 13:47:15 +0000</pubDate>
 <dc:creator>bcorfman</dc:creator>
 <guid isPermaLink="false">comment 4728 at http://community.activestate.com</guid>
</item>
<item>
 <title>going further...</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comment-4332</link>
 <description>&lt;p&gt;Cool.  Excellent quickstart guide.  If you want to get the plugin working for nose &amp;gt;= 0.10 or write some tests for the plugin (always a good idea!) then there are instructions and examples on this page of the nose docs: &lt;a href=&quot;http://somethingaboutorange.com/mrl/projects/nose/doc/writing_plugins.html&quot; title=&quot;http://somethingaboutorange.com/mrl/projects/nose/doc/writing_plugins.html&quot;&gt;http://somethingaboutorange.com/mrl/projects/nose/doc/writing_plugins.ht...&lt;/a&gt;&lt;/p&gt;
</description>
 <pubDate>Tue, 26 Feb 2008 22:52:30 +0000</pubDate>
 <dc:creator>kumar303</dc:creator>
 <guid isPermaLink="false">comment 4332 at http://community.activestate.com</guid>
</item>
<item>
 <title>The Impatient Developer&#039;s Guide to Writing Python Nose Plugins</title>
 <link>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins</link>
 <description>&lt;p&gt;In between fixing bugs for the impending 4.3 release, I found some time over the last couple of days to figure out how to build a plugin for Nose, a Python test framework that sits on top of unittest.py.  This entry will talk about the why and the how.&lt;/p&gt;
&lt;p&gt;First, we&#039;ve added a unit-test framework to Komodo.  The Python framework currently handles only tests based on unittest.py (aka PyUnit).  You have to tell it which tests to load, which is a bit awkward.  Nose has the smarts to find testable entities in a directory, so we don&#039;t need to duplicate that.&lt;/p&gt;
&lt;p&gt;Also, Nose supports plugins.  These plugins fall into two camps: plugins that find new things to test, and plugins that filter the output from the test runs.&lt;/p&gt;
&lt;p&gt;Some of you might know that Brandon Corfman wrote a Komodo extension for Nose, available at &lt;a href=&quot;http://community.activestate.com/xpi/knose&quot;&gt;http://community.activestate.com/xpi/knose&lt;/a&gt;. It&#039;s a good extension, but unfortunately doesn&#039;t work with the new Komodo framework.  Brandon also wrote it by parsing the raw output from Nose.  As I&#039;ve learned from adding Perl testing to Komodo, this is a hard path to follow.  It&#039;s always preferable to override an existing class, where you get to decide which output should be emitted for a set of events.  Whenever possible, choose to emit output over parsing input.&lt;/p&gt;
&lt;p&gt;The problem is that the documentation on writing Nose plugins is a bit terse, and makes a few assumptions on what the reader knows.  Here&#039;s what I worked out, the &quot;how&quot; of our story.  It&#039;s actually surprisingly short.&lt;/p&gt;
&lt;p&gt;You need to create two files (at least): your plugin, and a setup.py file.  I won&#039;t give all the details of the plugin (you can see the full code once it ships with Komodo), but I&#039;ll give the essentials here.&lt;/p&gt;
&lt;p&gt;First, you need some boilerplate to override the &lt;code class=&quot;geshifilter&quot;&gt;nose.plugins.Plugin&lt;/code&gt; class.  I defined the following class in a file called komodoplug.py:&lt;/p&gt;
&lt;div class=&quot;geshi-block&quot;&gt;
&lt;div class=&quot;geshifilter python&quot; style=&quot;font-family: monospace;&quot;&gt;&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; &lt;span style=&quot;color: #dc143c;&quot;&gt;traceback&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; nose&lt;br /&gt;
&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;from&lt;/span&gt; nose.&lt;span style=&quot;color: black;&quot;&gt;plugins&lt;/span&gt; &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; Plugin&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;class&lt;/span&gt; KomodoOutput&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;Plugin&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Output test results in a format suitable for Komodo&#039;s unit-testing&lt;br /&gt;
&amp;nbsp; &amp;nbsp; framework.&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;# Required attributes:&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;br /&gt;
&amp;nbsp; &amp;nbsp; name = &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;komodo&#039;&lt;/span&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;# invoke with --with-komodo argument to Nose&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; enabled = &lt;span style=&quot;color: #008000;&quot;&gt;True&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;geshi-plain&quot;&gt;import traceback
import nose
from nose.plugins import Plugin

class KomodoOutput(Plugin):
    &amp;quot;&amp;quot;&amp;quot;
    Output test results in a format suitable for Komodo&#039;s unit-testing
    framework.
    &amp;quot;&amp;quot;&amp;quot;
    # Required attributes:
    
    name = &#039;komodo&#039;   # invoke with --with-komodo argument to Nose
    enabled = True&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the same class, I then redefined the methods I wanted to provide output for:&lt;/p&gt;
&lt;div class=&quot;geshi-block&quot;&gt;
&lt;div class=&quot;geshifilter python&quot; style=&quot;font-family: monospace;&quot;&gt;&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;def&lt;/span&gt; addSuccess&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;, &lt;span style=&quot;color: #dc143c;&quot;&gt;test&lt;/span&gt;, capt&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;stream&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;writeln&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;passed&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;def&lt;/span&gt; addError&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;, &lt;span style=&quot;color: #dc143c;&quot;&gt;test&lt;/span&gt;, err, capt&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;stream&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;error: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;._writeFault&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;err&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;def&lt;/span&gt; addFailure&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;, &lt;span style=&quot;color: #dc143c;&quot;&gt;test&lt;/span&gt;, err, capt, tb_info&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;stream&lt;/span&gt;.&lt;span style=&quot;color: black;&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;failure: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008000;&quot;&gt;self&lt;/span&gt;._writeFault&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;err&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; def _writeFault(self, err):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.stream.writeln(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot;.join(self.formatErr(err)))&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; def formatErr(self, err):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exctype, value, tb = err&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return traceback.format_exception(exctype, value, tb)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;br /&gt;
&amp;nbsp; &amp;nbsp; def setOutputStream(self, stream):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # grab for own use&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.stream = stream&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # return dummy stream to suppress default output&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; class dummy:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; def write(self, *arg):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pass&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; def writeln(self, *arg):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pass&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; d = dummy()&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return d&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;br /&gt;
&amp;nbsp; &amp;nbsp; def startTest(self, test):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # There are some &amp;quot;&lt;/span&gt;start&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot; things Nose tells us about&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # that we don&#039;t want to hear about:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if isinstance(test, (nose.core.TestCollector,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;nose.suite.TestClass,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;nose.suite.TestDir,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;nose.suite.TestModule)):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.stream.write(&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #dc143c;&quot;&gt;test&lt;/span&gt; %s...&lt;span style=&quot;color: #483d8b;&quot;&gt;&amp;quot; % test)&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;geshi-plain&quot;&gt;def addSuccess(self, test, capt):
        self.stream.writeln(&amp;quot;passed&amp;quot;)

    def addError(self, test, err, capt):
        self.stream.write(&amp;quot;error: &amp;quot;)
        self._writeFault(err)
            
    def addFailure(self, test, err, capt, tb_info):
        self.stream.write(&amp;quot;failure: &amp;quot;)
        self._writeFault(err&amp;quot;)

    def _writeFault(self, err):
        self.stream.writeln(&amp;quot;&amp;quot;.join(self.formatErr(err)))

    def formatErr(self, err):
        exctype, value, tb = err
        return traceback.format_exception(exctype, value, tb)
    
    def setOutputStream(self, stream):
        # grab for own use
        self.stream = stream
        # return dummy stream to suppress default output
        class dummy:
            def write(self, *arg):
                pass
            def writeln(self, *arg):
                pass
        d = dummy()
        return d
    
    def startTest(self, test):
        # There are some &amp;quot;start&amp;quot; things Nose tells us about
        # that we don&#039;t want to hear about:
        if isinstance(test, (nose.core.TestCollector,
                             nose.suite.TestClass,
                             nose.suite.TestDir,
                             nose.suite.TestModule)):
            return
        self.stream.write(&amp;quot;test %s...&amp;quot; % test)&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The part that was new for me is that plugins need to be installed for Nose to find them.  Here&#039;s the setup.py file, located in the same directory as komodoplug.py.&lt;/p&gt;
&lt;div class=&quot;geshi-block&quot;&gt;
&lt;div class=&quot;geshifilter python&quot; style=&quot;font-family: monospace;&quot;&gt;&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; &lt;span style=&quot;color: #dc143c;&quot;&gt;sys&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;try&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; ez_setup&lt;br /&gt;
&amp;nbsp; &amp;nbsp; ez_setup.&lt;span style=&quot;color: black;&quot;&gt;use_setuptools&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;except&lt;/span&gt; &lt;span style=&quot;color: #008000;&quot;&gt;ImportError&lt;/span&gt;:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;pass&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;from&lt;/span&gt; setuptools &lt;span style=&quot;color: #ff7700;font-weight:bold;&quot;&gt;import&lt;/span&gt; setup&lt;/p&gt;
&lt;p&gt;setup&lt;span style=&quot;color: black;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; name=&lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;Komodo output plugin&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; version=&lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;0.1&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; author=&lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;Eric Promislow&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; author_email = &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;ericp@activestate.com&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; description = &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;Komodo output&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; license = &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;ActiveState Komodo&#039;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; py_modules = &lt;span style=&quot;color: black;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;komodoplug&#039;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;#93;&lt;/span&gt;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; entry_points = &lt;span style=&quot;color: black;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;nose.plugins&#039;&lt;/span&gt;: &lt;span style=&quot;color: black;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #483d8b;&quot;&gt;&#039;komodoplug = komodoplug:KomodoOutput&#039;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: black;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: black;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: black;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;geshi-plain&quot;&gt;import sys
try:
    import ez_setup
    ez_setup.use_setuptools()
except ImportError:
    pass

from setuptools import setup

setup(
    name=&#039;Komodo output plugin&#039;,
    version=&#039;0.1&#039;,
    author=&#039;Eric Promislow&#039;,
    author_email = &#039;ericp@activestate.com&#039;,
    description = &#039;Komodo output&#039;,
    license = &#039;ActiveState Komodo&#039;,
    py_modules = [&#039;komodoplug&#039;],
    entry_points = {
        &#039;nose.plugins&#039;: [
            &#039;komodoplug = komodoplug:KomodoOutput&#039;
            ]
        }

    )&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I then ran &lt;code class=&quot;geshifilter&quot;&gt;easy_setup .&lt;/code&gt; in the same directory as these two files, and then was ready to try it out.  I switched to the directory I downloaded &lt;code class=&quot;geshifilter&quot;&gt;BeautifulSoup&lt;/code&gt; into, and ran the following command:&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;nosetests --with-komodo  BeautifulSoupTests.py&lt;/pre&gt;&lt;p&gt;
and got my own test-result output.&lt;/p&gt;
&lt;p&gt;Let&#039;s try it with one of the Komodo source files:&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;C:&amp;gt;nosetests --with-komodo reflow.py
Done: run=0 errors=0 failures=0; T: 0.078 seconds&lt;/pre&gt;&lt;p&gt;
This isn&#039;t the output you&#039;ll get from the code snippet above, but it should give the idea that nothing happened.  That&#039;s because this module uses doctest for testing, so we need to tell Nose where to find the tests.  If I try this command-line:&lt;/p&gt;
&lt;pre class=&quot;geshifilter&quot;&gt;C:&amp;gt;nosetests --with-komodo --with-doctest reflow.py&lt;/pre&gt;&lt;p&gt;
I get the results of the doc-based tests in the output format my plugin generated.  This works because the Komodo plugin is an output-based plugin, while the doctest is a plugin that determines which tests to load.  The two plugins combine seamlessly.  Very cool.&lt;/p&gt;
</description>
 <comments>http://community.activestate.com/impatient-developers-guide-writing-python-nose-plugins#comments</comments>
 <pubDate>Sat, 23 Feb 2008 01:38:58 +0000</pubDate>
 <dc:creator>ericp</dc:creator>
 <guid isPermaLink="false">1962 at http://community.activestate.com</guid>
</item>
</channel>
</rss>
