satish
satish

Reputation: 943

Do I need to place __init__.py in each directory in a directory tree leading to my class?

I'm building a Django project with Python 3.6. I have created this directory structure ...

project
- manage.py
- scripts
  - run_commands.py
- commons
  - util
    - __init__.py
    - my_class.py

The contents of init.py are

from . import my_class

In another class, I attempt to import my MyClass like this

from commons.util import MyClass

but I'm getting this error

ModuleNotFoundError: No module named 'commons'

Am I creating my init.py properly?

Upvotes: 5

Views: 2187

Answers (2)

jhrr
jhrr

Reputation: 1730

It used to be the case that yes, you need to put an __init__.py in every directory that is going to be treated as a python module as without an __init__.py python wouldn't look inside that directory for importable code.

- project
  - __init__.py
  - commons
    - __init__.py
    - util
      - __init__.py
      - my_class.py

But as Reinstate Monica points out below this is no longer true as of Python 3.3+. So, depending on your version of Python you will need to make an informed decision.

Note, you might or might not need an __init__.py in the root project directory (if you need them at all), it depends if it has definitions that are part of the source tree. But you won't need it if it's just a container, like you see in the way most Django projects are organised, for example.

Upvotes: 2

Lord Elrond
Lord Elrond

Reputation: 16032

It looks like the problem is that MyClass is not located in commons.util, because you only imported the module named my_class, not the class itself.

Instead the file commons/util/__init__.py should contain the following import:

from .my_class import MyClass

I don't think this will solve your problem, because you would be getting a different error than the one shown, but you will get errors for this eventually.

Update

First, I'd recommend reading this answer for a good explanation for how imports work in python.

Basically, when you execute from commons.util import MyClass, the interpreter scans the contents of sys.path for a module named commons.
I assume you didn't set sys.path to include your project folder, hence the ModuleNotFoundError.

TLDR; you have 2 options:

  • Manually set sys.path in run_commands.py to check your project folder (Don't do this!)
  • Use Django's Command class

To use Django's Command class, you will need to adjust your project folder similar to the following:

project
- manage.py
- commons
- management
  - commands
    run_commands.py     
  - util
    - __init__.py
    - my_class.py

Now in run_commands.py:

from django.core.management.base import BaseCommand

from commons.util import MyClass

class Command(BaseCommand):
    def handle(*args, **options):
        print("handling", MyClass)

You can execute the new command with the following:

python3 manage.py run_commands

Upvotes: 4

Related Questions