Import name conflicts

Steve D'Aprano steve+python at
Tue Apr 18 19:58:57 EDT 2017

On Wed, 19 Apr 2017 04:28 am, Tim Johnson wrote:

> Using python 2.7~
> For testing and edification purposes:
> I have a project which has a controllers package at the directory
> level just below the root.

Do you mean the root of the file system?

>>From the top-level (root) of the project, I start my python
> interpreter.

Don't do that. You are defeating the whole purpose of packages.

The point of a package is to the caller, it appears like a single library,
while still allowing it to contain multiple files internally with its own
fine structure.

If your package structure looks like this:

  ... any other modules and sub-packages

then you should start Python from X, not from X/controllers.

Or even better, you should ensure X is in sys.path (there are various ways
to do that), and then start Python from anywhere.

> The packages is called 'controllers' and has a submodule named 'imp'
> I do the following:
>>>> a = __import__('imp')
>>>> dir(a)
> ['C_BUILTIN', 'C_EXTENSION', 'IMP_HOOK', 'NullImporter',
> 'PKG_DIRECTORY', ....]
> # object a is the builtin imp module

Like (nearly) all __dunder__ methods and functions, __import__ is reserved
for use by Python and you shouldn't need to use it.

The conventional ways to access packages.imp are (untested):

# As a user of the controllers package
import controllers.imp as a

# Within the controllers package
from __future__ import absolute_imports
from . import imp as a

You only need the "from __future__ ..." directive once per module, but it
must be the very first line of executable code. (It can only be preceded by
blank lines or comments.)

To get access to the standard library imp, you do this:

# As a user of the controllers package
import imp as b

# Within the controllers package
import imp as b

> Now, suppose a python upgrade provides a package called controllers
> or there's some great third-party package available called
> controllers.

If you have two modules/packages with the same name, there's no clean way to
access one or the other. That's not how importing works. Whichever module
is found in the search path (sys.path) shadows the other.

That's both a feature and an annoyance, depend on whether you find it useful
or not. But that's how it is.

In the case of name conflicts between modules, your choices are:

- live with the conflict;
- rename the conflicting module;
- manually adjust sys.path when and as needed to avoid one and 
  get the other, then reverse your adjustments to do the opposite.

I don't recommend the third option. There's no clean way to do it. But if
you absolutely must, sys.path is just a list of places to search, you can
modify it at will. And sys.modules is just a dict of {name: module} caching
the modules, you need to modify that as well.

“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

More information about the Python-list mailing list