5

I have following project hierarchy:

project_dir
  lib
    __init__.py
    ...
    some_script.py
    ...
    agent
      __init__.py
      ...
      errors.py
      some_agent_script.py
      ...

There is the SomeException class definition in lib/agent/erros.py I use following code to import them inside lib/agent/some_agent_script.py:

from errors import SomeException

Also I use following code to import in lib/some_script.py

from agent.errors import SomeException

The problem is when I raise a SomeException in lib/agent/some_agent_script.py then lib/some_script.py can not catch it in an except block:

try:
    # Here comes a call to lib/agent/some_agent_script.py function
    # that raises SomeException
except SomeException, exc:
    # Never goes here
    print(exc)
except Exception, exc:
    print(exc.__class__.__name__) # prints "SomeException"

    # Let's print id's
    print(id(exc.__class__))
    print(id(SomeException))
    # They are different!

    # Let's print modules list
    pprint.pprint(sys.modules)

I can see in sys.modules that erros module was imported twice: the first is with 'agent.errors' key and the second is with 'lib.agent.errors' key

The following code goes right, but it's not a beautiful solution:

agent_errors = sys.modules.get('agent.errors')
from agent_errors import SomeException

try:
    # Here comes a call to lib/agent/some_agent_script.py function
except SomeException:
    print('OK')

What should I do to make this module not to import twice?

fey
  • 1,289
  • 1
  • 10
  • 20
  • 2
    Related reading: [SO question](http://stackoverflow.com/questions/1459236/module-reimported-if-imported-from-different-path), [PEP 395](http://www.python.org/dev/peps/pep-0395/) – Lev Levitsky Nov 02 '12 at 11:11
  • 1
    That's why they introduced relative imports. – Bakuriu Nov 02 '12 at 12:41

1 Answers1

2

You should always use fully qualified imports.

from lib.agent.errors import SomeException

Do this in every module that uses it. Then it will always have the same package name. You should probably also change the top-level package name. The name "lib" is too generic.

This will also save you some headaches if you happen to name a module the same as a base, or "stock", module. For example, suppose you created a module lib/agent/socket.py and in lib/agent/some_agent_script.py you wrote import socket. You would not actually get your module, but the stock one.

So it's better to get in the habit of always using fully qualified package names, off a common root if you can.

An alternative is to use absolute imports.

from __future__ import absolute_import

import .errors

Note the leading dot. This explicitly imports from the current package. It should also fix your problem, but I admit I haven't tried it.

Keith
  • 42,110
  • 11
  • 57
  • 76
  • Sounds good, but lib/agent is a library which is being used in several projects. It's not good to specify fully qualified name there, cause in other projects agent can be located not in lib module. – fey Nov 02 '12 at 13:48
  • Thanks! That's just what I needed! There is an addition: it is nesessary to use absolute_import in both modules - which are raising and handling an exception. – fey Nov 02 '12 at 15:24
  • @fey Cool. Yes, all `__future__` imports only affect the module they are in. – Keith Nov 02 '12 at 15:59