7

I'm building a package that contains scripts to be run. They import modules contained in a subfolder directly under the script. Now that __init__ is not required after Python 3.3, what's the correct file structure and import statement to have? I'd like to not have to specify the import from the topmost folder down, only as a relative path, here sub/module.

This is the current state of the file structure:

Root\
    src\
        sub\
            module.py
        script.py
        parent_module.py
    setup.py

# Inside script.py
import sub.module      # Doesn't work
from sub import module # Doesn't work
import src.sub.module  # Does work!

import .sub.module     # Doesn't work
import .parent_module  # Does work!

I imagine I need to have some __init__ file, but what and where would that be? Any help is greatly appreciated, I don't know much about packaging.

Also, I'm certainly open to suggestions to changing the structure, if that makes things easier.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
Felix
  • 2,548
  • 19
  • 48
  • What is your Python version? (run `python -V` to check) – kristaps Jul 18 '18 at 09:00
  • @kristaps Python 3.6.5, so very much after 3.3.x – Felix Jul 18 '18 at 09:01
  • 1
    Note that not having ``__init__.py`` still makes a difference - it marks a namespace package. For a regular package, you should continue adding ``__init__.py`` as before. – MisterMiyagi Jul 18 '18 at 09:04
  • 2
    How are you running script.py? I am getting completely different results. – itsadok Jul 18 '18 at 09:18
  • 1
    @itsadok That is a **valuable** comment. Never rely on IDE errors before executing an interpreted language. PyCharm gives an error, program executes just fine. – Felix Jul 18 '18 at 09:20

1 Answers1

4

The missing __init__.py are not the problem - you are using outdated relative imports.

import sub.module         # implicit relative import - py2 only
from . import sub.module  # explicit relative import

Note that a . import always requires the from .<where> import <name> form. It would not produce a valid name otherwise. The following should work, assuming your run script.py via python3 -m src.script - an IDE will likely do the same.

from . import sub.module
from .sub import module
from .sub.module import *
from . import parent_module

If you are running as plain python3 script.py or python3 -m script, you cannot use relative imports. Only absolute imports will work in this case.

import sub.module
from sub import module
from sub.module import *
import parent_module

While you do not need __init__.py files, it is a good idea to add them if your package is not a namespace. Otherwise, other similarly constructed packages of the same name may be inserted into yours.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • Thank you very much for the answer! It seems the absolute imports are the way to go. Now that I've been [struggling](https://softwareengineering.stackexchange.com/questions/375324/referencing-custom-python-modules-and-data-files) with this for a while, may I ask a few direct questions that may not be in the best style of these forums? I hope you'll humor me. Do these absolute imports work if this is installed with setuptools and executed as a cmd script? If not, how would you, however briefly explained, install and execute scripts that need custom modules to run to a different environment? – Felix Jul 18 '18 at 09:34
  • PyCharm already is a *bit* different with its script execution, so I'd like to be sure. – Felix Jul 18 '18 at 09:35
  • @Felix Installing with setuptools is fine - you can then use absolute imports in any case. I suggest using the console_scripts entry point, though, since it allows relative imports for both development and deployment. See https://stackoverflow.com/q/18787036/5349916 and http://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point for reference. – MisterMiyagi Jul 18 '18 at 09:46