0

In a project one module moduleA requires access to module moduleB within the same sub-package packageA (which is in package project). This access fails, when the __init__.py of sub-package packageA is filled with an import .. as .. statement, while the __init__py of package project is empty.

Why does a filled __init__.py (seemingly) block access from this (same package) modules - while PyCharm seems to still accept it from an autocomplete and highlighting perspective? The thrown AttributeError suggests, that the import .. as .. statement makes the interpreter believe that the sub-package is an attribute, not an package – despite an existing __init__.py.

File structure

├── ProjectA
│   ├── src
│   │   ├── project
│   │   │   ├── __init__.py
│   │   │   ├── packageA
│   │   │   │   ├── __init__.py
│   │   │   │   ├── moduleA.py
│   │   │   │   ├── moduleB.py

Code sample 1

# ProjectA / src / project / __init__.py
(empty)

# packageA / __init__.py
(empty)

# packageA / moduleA.py
import project.packageA.moduleB as dummy

class A:
    pass

class B:
    pass

# packageA / moduleB.py
def method():
    pass

Code execution 1

# jupyter stated in 'C:\\Users\\username\\Desktop\\devenv\\'
# notebook located in 'C:\\Users\\username\\Desktop\\devenv\\dev\\'
import sys
sys.path
# output:
# ['C:\\src\\ProjectA',
#  'C:\\src\\ProjectA\\src',
#  'C:\\Users\\username\\Desktop\\devenv\\dev',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\python36.zip',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\DLLs',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv',
#  '',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib\\site-packages',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib\\site-packages\\win32',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib\\site-packages\\win32\\lib',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib\\site-packages\\Pythonwin',
#  'C:\\ProgramData\\Anaconda3\\envs\\myenv\\lib\\site-packages\\IPython\\extensions',
#  'C:\\Users\\username\\.ipython']

from project.packageA.moduleA import A, B
# no error(s)

Code sample 2

First alternative filling of packageA / __init__.py

# packageA / __init__.py
from .moduleA import A, B
import .moduleB as dummy

Second alternative filling of packageA / __init__.py

# packageA / __init__.py
from project.packageA.moduleA import A, B
import project.packageA.moduleB as dummy

Code execution 2

from project.packageA.moduleA import A, B
AttributeError                            Traceback (most recent call last)
<ipython-input-1-61a791f79421> in <module>
----> 1 import project.packageA.moduleA.moduleB

C:\src\ProjectA\src\project\packageA\__init__.py in <module>
----> 1 from .moduleA import A, B
      2 from .moduleB import *

C:\src\ProjectA\src\project\packageA\moduleA.py in <module>
---> 1 import project.packageA.moduleB as dummy
     2 
     3 class A:

AttributeError: module 'project' has no attribute 'packageA'

Solution

I've found the solution in Stack Overflow: Imports in __init__.py and import as statement

Changing the import in packageA / __init__.py from import .. as to from xx import .. as did the trick:

# packageA / __init__.py
from project.packageA.moduleA import A, B
from project.packageA import moduleB as dummy

Can anyone help me to understand, why import xx as and from xx import xx as work differently, when it comes to sub-packages - specifically in this situation where the package's __init__.py is empty, but the sub-package's __init__.py is filled?

Nex
  • 421
  • 1
  • 4
  • 17

1 Answers1

0

This behavior can actually not be described by any features of intended back-end mechanisms: the behavior doesn't match any of the documentation, e.g. PEP 0221. Thus the import .. as .. statement is nonfunctional. This bug seems to have been fixed with Python 3.7 (I've been running 3.69)

Nex
  • 421
  • 1
  • 4
  • 17