Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python import error: 'module' object has no attribute 'x'

I am trying to do a python script that it is divided in multiple files, so I can maintain it more easily instead of making a very-long single file script.

Here is the directory structure:

wmlxgettext.py
<pywmlx>
  |- __init__.py
  |- (some other .py files)
  |- <state>
       |- __init__.py
       |- state.py
       |- machine.py
       |- lua_idle.py

if I reach the main directory of my project (where the wmlxgettext.py script is stored) and if I try to "import pywmlx" I have an import error (Attribute Error: 'module' object has no attribute 'state')

Here is the complete error message:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/user/programmi/my/python/wmlxgettext/true/pywmlx/__init__.py", line 9, in <module>
    import pywmlx.state as statemachine
  File "/home/user/programmi/my/python/wmlxgettext/true/pywmlx/state/__init__.py", line 1, in <module>
    from pywmlx.state.machine import setup
  File "/home/user/programmi/my/python/wmlxgettext/true/pywmlx/state/machine.py", line 2, in <module>
    from pywmlx.state.lua_idle import setup_luastates
  File "/home/user/programmi/my/python/wmlxgettext/true/pywmlx/state/lua_idle.py", line 3, in <module>
    import pywmlx.state.machine as statemachine
AttributeError: 'module' object has no attribute 'state'

Since I am in the "project main directory" pywmlx should be on PYTHONPATH (infact I have no troubles when I tried to import pywmlx/something.py)

I'm not able to figure where is my error and how to solve this problem.

Here is the pywmlx/__init__.py source:

# all following imports works well:
from pywmlx.wmlerr import ansi_setEnabled
from pywmlx.wmlerr import wmlerr
from pywmlx.wmlerr import wmlwarn
from pywmlx.postring import PoCommentedString
from pywmlx.postring import WmlNodeSentence
from pywmlx.postring import WmlNode 

# this is the import that does not work:
import pywmlx.state as statemachine

Here is the pywmlx/state/__init__.py source:

from pywmlx.state.machine import setup
from pywmlx.state.machine import run

But I think that the real problem is somewhat hidden in the "imports" used by one (or all) python modules stored in pywmlx/state directory.

Here is the pywmlx/state/machine.py source:

# State is a "virtual" class
from pywmlx.state.state import State
from pywmlx.state.lua_idle import setup_luastates
import pywmlx.nodemanip as nodemanip

def addstate(self, name, value):
    # code is not important for this question
    pass

def setup():
    setup_luastates()

def run(self, *, filebuf, fileref, fileno, startstate, waitwml=True):
    # to do
    pass

Finally here is the pywmlx/state/lua_idle.py source:

import re
import pywmlx.state.machine as statemachine
# State is a "virtual" class
from pywmlx.state.state import State

# every state is a subclass of State
# all proprieties were defined originally on the base State class:
    # self.regex and self.iffail were "None"
    # the body of "run" function was only "pass"
class LuaIdleState (State):
    def __init__(self):
        self.regex = re.compile(r'--.*?\s*#textdomain\s+(\S+)', re.I)
        self.iffail = 'lua_checkpo'

    def run(xline, match):
        statemachine._currentdomain = match.group(1)
        xline = None
        return (xline, 'lua_idle')


def setup_luastates():
    statemachine.addstate('lua_idle', LuaIdleState)

Sorry if I posted so much code and so many files... but I fear that the files, in directory, hides more than a single import problem, so I published them all, hoping that I could explain the problem avoiding confusion.

I think that I miss something about how import works in python, so I hope this question can be useful also to other programmers, becouse I think I am not the only one who found the official documentation very difficult to understand when explaining import.


Searches Done:

Not Useful: I am already explicitly using import x.y.z all times I need to import something

Not Useful: Even if the question asks about import errors, it seems not useful for the same reason as (1)

Not Useful: As far as I know, pywmlx should be located into PYTHONPATH since "current working directory" on my tests is the directory that contains the main python script and pywmlx directory. Correct me if I am wrong

like image 306
Nobun Avatar asked Jan 25 '16 11:01

Nobun


2 Answers

You are having a circular import in your framework. Circular imports do not work well with aliases. When importing a module with an alias and then, during the circular import, importing it again without an alias, python complains. The solution is to not use aliases (the "import module as" syntax) but always use the full "import module" statement.

like image 199
Serbitar Avatar answered Sep 23 '22 17:09

Serbitar


Python does several things when importing packages:

  • Create an object in sys.modules for the package, with the name as key: 'pywmlx', 'pywmlx.state', 'pywmlx.state.machine', etc.
  • Run the bytecode loaded for that module; this may create more modules.
  • Once a module is fully loaded and it is located inside another package, set the module as an attribute of the parent module object. Thus the sys.modules['pywmlx.state'] module is set as the state attribute on the sys.modules['pywmlx'] module object.

That last step hasn't taken place yet in your example, but the following line only works when it has been set:

import pywmlx.state.machine as statemachine

because this looks up both state and machine as attributes first. Use this syntax instead:

from pywmlx.state import machine as statemachine

Alternatively, just use

import pywmlx.state.machine

and replace statemachine. everywhere else with pywmlx.state.machine.. This works because all that is added to your namespace is a reference to the sys.modules['pywmlx'] module object and the attribute references won't need to be resolved until you use that reference in the functions and methods.

like image 33
Martijn Pieters Avatar answered Sep 26 '22 17:09

Martijn Pieters