-
Notifications
You must be signed in to change notification settings - Fork 0
Having issues with imports?
Instead, always prefix your step files with step
or steps
. If you have registration.feature
, name the stepfile steps_registration.py
(etc.).
Here's something I just banged my head against for a while.
I am writing a Django project test-first. I was about to add features related to user registration and so on. So I created features/registration.feature
and features/steps/registration.py
.
Then I wrote code for it. The code used django-registration. Everything seemed in order, so I ran behave on the feature again. That produced the following error:
...
File "/home/h/workspace/showroom/djangoproject/apps/users/urls.py", line 9, in <module>
from registration import forms
ImportError: cannot import name forms
Huh. I look at the relevant snippet in my users.urls
module:
from registration.forms import (
RegistrationFormNoUsername
)
Nothing too strange here. What if I just do import registration
? I try it: it works. Now I decide to do some manual testing. Confusingly, there are no issues. No import issues. The module works fine and I am seeing what I expect (and exactly what registration.feature
and steps/registration.py
specify).
So it must be import funkiness. Submodules of registration
won't import. So I tried using pkgutil to list the submodules (based on this snippet), something like this:
def inspect_submodules_of_imported_package():
import pkgutil
# import the package to inspect, i.e. registration (django-registration)
import registration
package = registration
msgs = []
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
msgs.append("Found submodule %s (is a package: %s)" % (modname, ispkg))
raise Exception(msgs) # stop process, print informnation about sumbodules
inspect_submodules_of_imported_package()
But this produced an error too:
File "/home/h/workspace/showroom/djangoproject/apps/users/urls.py", line 15, in <module>
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
AttributeError: 'module' object has no attribute '__path__'
If the thing you're importing is a package, it should have a __path__
attribute. However if it is a single module and not a package, it will not have a __path__
attribute (see here). So registration
is not a package (does not have submodules)? I triple-checked that I installed it correctly, so what's going on here?
The answer lied in the module's __file__
attribute. I replaced the snippet above with this one:
def locate_imported_module():
import registration
raise Exception(registration.__file__)
locate_imported_module()
This produced the following output:
File "/home/h/workspace/showroom/djangoproject/apps/users/urls.py", line 13, in <module>
raise Exception(registration.__file__)
Exception: /home/h/workspace/showroom/djangoproject/features/steps/registration.pyc
Aha! The problem was that I had named my feature (and step) something that conflicted with the registration
module. As a result users.urls
was actually trying to import features.steps.registration
instead of the registration
we wanted (django-registration). What threw me for a loop was that this was happening only when running behave tests, because outside of behave's case, features/steps/registration.py
is not on my path (and thus, doesn't conflict).
Don't name a step module the same thing as another module, ever!
There's an easy way to avoid naming conflicts all the time: just add a prefix or suffix to all of the step files' names.
Instead of naming this file steps/registration.py
, name it steps/steps_registration.py
. At the time of writing, the behave tutorial does not make this clear. (But jenisys's excellent tutorial does).