buzzysin
buzzysin

Reputation: 331

Custom Python module structure

I am currently doing a personal coding project and I am trying to build a module, but I don't know why my structure doesn't work the way it's supposed to:

\mainModule
    __init__.py
    main.py
    \subModule_1
        __init__.py
        someCode_1.py
        someCode_2.py
    \subModule_2
        __init__.py
        otherCode.py

I want to be able to run the following code from main.py:

>>> from subModule_1 import someCode_1
>>> someCode_1.function()
"Hey, this works!"
>>> var = someCode_2.someClass("blahblahblah")
>>> var.classMethod1()
>>> "blah blah blah"
>>> from subModule2 import otherCode
>>> otherCode("runCode","#ff281ba0")

However, when I try to import someCode_1, for example, it returns an AttributeError, and I'm not really sure why. Is it to do with the __init__.py file?

REVISIONS

** The exact error is:

Traceback (most recent call last):
  File "C:\...\mainDir\main.py", line 2, in <module>
    subDir.codeFile.function()
AttributeError: module 'subDir' has no attribute 'codeFile'

Credits to @jonrsharpe: Thanks for showing me how to use Stack Overflow correctly.

Upvotes: 2

Views: 154

Answers (2)

jez
jez

Reputation: 15349

When you import subDir, it does three things:

  1. executes the code in mainDir/subDir/__init__.py (i.e. in this case does nothing, because this file is empty)
  2. imports the resulting module under the name subDir locally, which will in turn make it an attribute of the mainDir module;
  3. registers the new import globally in the sys.modules dictionary (because the import is being performed from a parent module mainDir, the name is completed to 'mainDir.subDir' for the purposes of this registration);

What it does not do, because it hasn't been told to, is import subDir.codeFile. Therefore, the code in codeFile.py has not been run and the name codeFile has not yet been imported into the namespace of mainDir.subDir. Hence the AttributeError when trying to access it. If you were to add the following line to mainDir/subDir/__init__.py then it would work:

import codeFile

Specifically, this will:

  1. run the code in codeFile.py
  2. add the resulting module as an attribute of the mainDir.subDir module
  3. store a reference to it as yet another entry in sys.modules, this time under the name mainDir.subDir.codeFile.

You could also achieve the same effect from higher up the module hierarchy, by saying import subDir, subDir.codeFile instead of just import subDir in your mainDir.main source file.

NB: When you test this from the command line or IDE, make sure that your current working directory (queried by os.getcwd(), changed using os.chdir(wherever) ) is neither mainDir nor subDir. Work from somewhere else—e.g. the parent directory of mainDir. Working from inside a module will lead to unexpected results.

Upvotes: 1

sandor
sandor

Reputation: 671

You have two options to make this work.

Either this:

from subdir import codeFile
codeFile.function()

Or:

import subdir.codeFile
subdir.codeFile.function()

Upvotes: 1

Related Questions