views:

84

answers:

3

EDIT: OK, I managed to isolate the bug and the exact, complete code to to reproduce it. But it appears either something that's by design, or a bug in python.

Create two sibling packages: admin & General, each with it's own __init__.py, of course. In the package admin put the file 'test.py' with the following code:

from General.test02 import run
import RunStoppedException
try:
    run()
except RunStoppedException.RunStoppedException,e:
    print 'right'
except Exception,e:
    print 'this is what i got: %s'%type(e)

and also in admin put the file 'RunStoppedException.py' with the following code:

class RunStoppedException(Exception):
    def __init__(self):
        Exception.__init__(self)

In the package General put the file test02.py with the code:

import admin.RunStoppedException
def run():
    raise admin.RunStoppedException.RunStoppedException()

the printout:

this is what i got: <class 'admin.RunStoppedException.RunStoppedException'>

When it should've been right. This only happens when one file sits in the same dir as the exception, so they import it differently.

Is this by design, or a bug of python?

I am using python2.6, running it under eclipse+pydev

A: 

I can only see two reasons

  1. You have two different Exception classes with same name
    Edit: I think culprit is this part because you import Exception class two ways

    • from RunStoppedException import RunStoppedException
    • from admin.RunStoppedException import RunStoppedException

    make them consistent and your problem will be gone.

  2. You are using some IDE, which is interfering with your code, this sounds bizarre but try to run your code on command line if you aren't

Even 1 and 2 doesn't fix your problem, write a small piece of code demonstrating the problem, which we can run here, which we can fix, but I am sure we will not need to because once you have written such a small standalone script where you can replicate the problem you will find the solution too.

Anurag Uniyal
A: 

Works fine for me:

[/tmp] ls admin/
RunStoppedException.py  __init__.py     test.py
RunStoppedException.pyc __init__.pyc
[/tmp] ls General/
__init__.py __init__.pyc    test02.py test02.pyc
[/tmp] python -m admin.test 
right
[/tmp] 

Running on:

Python 2.6.4 Stackless 3.1b3 060516 (release26-maint, Dec 14 2009, 23:28:06) 
[GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin

My guess is that you have another "General" on your path somewhere, perhaps from earlier tests, and thats why the exceptions don't match. Did you try the id/inspect.getabsfile debugging? If so, what was the output?

truppo
did you write your answer before I changed my question, since you can run the code for yourself now :) It appears something deeper, that the two inspections above won't resolve...
noam
Edited now with test of your example
truppo
+5  A: 
import admin.RunStoppedException

This is an ambiguous relative import. Do you mean RunStoppedException from the admin top-level module? Or from mypackage.admin when you're in a package? If your current working directory (which is added to the module search path) happens to be inside the package, it could be either, depending on whether Python knows it's inside a package, which depends on how you're running the script.

If you've got both import admin.RunStoppedException and import RunStoppedException in different modules, that could very well import two copies of the same module: a top-level RunStoppedException and a submodule admin.RunStoppedException of the package admin, resulting in two instances of the exception, and the subsequent mismatch in except.

So don't use implicit relative imports. They are in any case going away (see PEP328). Always spell out the full module name, eg. import mypackage.admin.RunStoppedException. However avoid using the same identifier for your module name and your class name as this is terribly confusing. Note that Python will allow you to say:

except RunStoppedException:

where that identifier is referring to a module and not a subclass of Exception. This is for historical reasons and may also go away, but for the meantime it can hide bugs. A common pattern would be to use mypackage.exceptions to hold many exceptions. One-class-per-file is a Java habit that is frowned on in Python.

It's also a good idea generally try to keep the importing of module contents (like classes) down as much as possible. If something changes the copy of RunStoppedException inside the module, you'll now have different copies in different scripts. Though classes mostly don't change, module-level variables may, and monkey-patching and reloading become much harder when you're taking stuff outside of its owner module.

bobince
I have changed the code to refer explicitly to each location (see above) - still no dice. So I think your guess is wrong, though I agree that naming the module and class with the same name is bad practice
noam
Updated answer re update! :-)
bobince
@bobince - Thanks! This looks like the right diagnosis for the problem (gonna dig into it a little further), only thing is, I don't get where am I making an "implicit" import. I am explictly spelling out, as best as I can, the path of the module. So I understand the problem, but not how to solve it, in this case (can't go any more explicit in my imports:)
noam
The `import RunStoppedException` in `admin.test` in the new example is not explicit. If the top-level package is `admin`, it should be `import admin.RunStoppedException`.
bobince