msudder
msudder

Reputation: 512

module name overloading and import path

The code below fails because twisted.trial.unittest.TestCase, the desired baseclass, is not the baseclass.

from twisted.trial import unittest
from unittest import TestCase
import myapp

class Feature(TestCase):

  def setUp(self):
    self.callbackCounter = 0

  def checkCbCalled(self, expected):
      self.assertEqual(self.callbackCounter, expected)

  def testTrialCallsDeferred(self):

      d = myapp.buildFeature()
      self.addCleanup(self.checkCbCalled, expected=1)
      def cb(res):
        self.callbackCounter += 1
      d.addCallback(cb).addErrback(self.fail)
      return d           # does not fire because of 'import rules'?

If I had said

from twisted.trial import unittest as trialut
from trialut import TestCase 

or, better:

from twisted.trial.unittest import TestCase

then the test would run as expected and trial.unittest.TestCase would have fired my deferred.

This seems as though the local recently imported thing should have superceded the one available in {lib/pythonX.X/unittest}. I understand it must be a rule based on sys.path or something else implicit or explicit. This tripped me up for a little too long because I did not have the call to addCleanup and all tests were passing because returned deferred instance was not being fired.

I broke some rule(s), Please advise some reading or other.

Thanks Mike

Upvotes: 1

Views: 249

Answers (1)

Jean-Paul Calderone
Jean-Paul Calderone

Reputation: 48335

It's very unclear how you expect this to work, but it sounds like you're at least somewhat confused about how modules in Python behave.

An import statement (sometimes) loads a module from a source file and then binds a name in the local scope to that module object.

So, when you do:

from twisted.trial import unittest

You're loading the twisted.trial.unittest module and then binding the local name unittest it.

This interacts in no interesting way with the following statement:

from unittest import TestCase

which loads the unittest module and then binds the local name TestCase to the object referred to by that module's TestCase attribute.

When you later subclass TestCase:

class Feature(TestCase):
    ...

you're making straightforward use of the name TestCase in your local scope - a name which refers to the TestCase class defined by the unittest module. Notice that it has nothing at all to do with Twisted's twisted.trial.unittest module, even though you've loaded that module too. You have to use one of its attributes in order to use its functionality.

One change to make to improve the behaviour of your code is to simply stop using the standard libraryunittest module at all. Delete this line:

from unittest import TestCase

And replace your class definition with:

class Feature(unittest.TestCase):

Don't get confused by the fact that the standard library unittest module shares part of the name of the Twisted twisted.trial.unittest module. They are different modules with different (though similar, sometimes overlapping) features. The class definition in the example above is using twisted.trial.unittest, because it comes after your original line:

from twisted.trial import unittest

Once you are actually using Twisted's TestCase (from twisted.trial.unittest) and not the standard library's TestCase (from unittest), you'll get the behaviour you expect when a test method returns a Deferred. This is because it's Twisted that provides this feature, not the standard library.

Upvotes: 2

Related Questions