Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Package import failure in Python 3.5

I have the following folder structure:

/main
    main.py
    /io
        __init__.py
        foo.py

In Python 2.7 I would write the following in main.py:

import io.foo

or

from io.foo import *

wheareas in Python 3.5 I get an import error:

Traceback (most recent call last):
    File "./main.py", line 6, in <module>
    import io.foo
ImportError: No module named 'io.foo'; 'io' is not a package

I couldn't find any help so far.

like image 244
Torilla Avatar asked Apr 06 '18 11:04

Torilla


People also ask

What is __ init __ py?

The __init__.py files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string , unintentionally hiding valid modules that occur later on the module search path.

What is __ all __ in Python?

In the __init__.py file of a package __all__ is a list of strings with the names of public modules or other objects. Those features are available to wildcard imports. As with modules, __all__ customizes the * when wildcard-importing from the package.

What is import * in Python?

In Python, you use the import keyword to make code in one module available in another. Imports in Python are important for structuring your code effectively. Using imports properly will make you more productive, allowing you to reuse code while keeping your projects maintainable.

What is import module in Python?

Python code in one module gains access to the code in another module by the process of importing it. The import statement is the most common way of invoking the import machinery, but it is not the only way. Functions such as importlib.


2 Answers

io is a built-in module. Don't name your local packages the same as a built-in module.

like image 125
Erik Cederstrand Avatar answered Sep 21 '22 20:09

Erik Cederstrand


While @ErikCederstrand's answer is correct and probably sufficient for you, I was curious as to why it failed so I went digging through cpython's source. So for any future visitors, here's what I found.

The function where it's failing is here: https://github.com/python/cpython/blob/3.4/Lib/importlib/_bootstrap.py#L2207

On line 2209, it checks to see if the parent module has been loaded:

parent = name.rpartition('.')[0]  #  Value of 'io'

Since it has loaded the builtin io module, it continues on like normal. After the if returns false, it goes on to assign parent module which again is set to "io":

if name in sys.modules:
    return sys.modules[name]
parent_module = sys.modules[parent]

The next lines are what cause the failure, and it's because builtin modules (io anyway) don't have a __path__ instance variable. The exception you see raised here are ultimately what you're seeing when you run it:

try:
    path = parent_module.__path__
except AttributeError:
    msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent)
    raise ImportError(msg, name=name)

If you change your module name like Erik says, then step through this whole process, you can see the call to get parent_module.__path__ works like it's supposed to and everything's happy.

So, tldr: you've tricked the import system into thinking it's already loaded your custom module, but when it goes to try and use it like a custom module it fails because it's actually the builtin io.

EDIT: It looks like __path__ is set here after it goes through a normal import process in init_module_attrs:

 if _override or getattr(module, '__path__', None) is None:
     if spec.submodule_search_locations is not None:
         try:
             module.__path__ = spec.submodule_search_locations
         except AttributeError:
             pass
like image 35
wholevinski Avatar answered Sep 21 '22 20:09

wholevinski