0

I'm using PyCharm and Python 3 and I have the next python folder layout:

src/
    __init__.py
    command/
        __init__.py
        simpleremote/
            __init__.py
            Command.py
            GarageDoor.py
            GarageDoorOpenCommand.py
            Light.py
            LightOffCommand.py
            LightOnCommand.py
            RemoteControlTest.py
            SimpleRemoteControl.py

I created packages, as you can see. The file with the main method is RemoteControlTest.py which worked perfectly with these imports:

from pythonDesignPatterns.src.command.simpleremote.GarageDoor import GarageDoor
from pythonDesignPatterns.src.command.simpleremote.GarageDoorOpenCommand import GarageDoorOpenCommand
from pythonDesignPatterns.src.command.simpleremote.Light import Light
from pythonDesignPatterns.src.command.simpleremote.command import LightOnCommand
from pythonDesignPatterns.src.command.simpleremote.SimpleRemoteControl import SimpleRemoteControl

Previous to create the init.py files for the packages, I tried to use relative imports instead of the former ones, for example

from .command import LightOnCommand

but it gave me an error (going to call this Case A):

SystemError: Parent module '' not loaded, cannot perform relative import. 

so fiddling around I found out that this worked for the same line:

from command.simpleremote.Command import LightOnCommand

and the program was executed succesfully again, but if I use the "Refactor" option from the IDE (PyCharm) and rename "Command" to "command" , writing this line:

from command.simpleremote.command import LightOnCommand

it suddenly shows the error (Case B):

ImportError: No module named 'command.simpleremote'; 'command' is not a package

Which is the problem in each case (A and B)? I can't understand why it would work in between each cases when importing from command.simpleremote. Why not one level further or closer? Why is it working with capital 'C' and not 'c'? Is this case-sensitive?

I've looked at official Python docs, (and webs for PEP302, PEP328 and PEP420) but it's too complex for me in a single night. Can anyone make understand this in a simpler way (or tell me a resource I can read about this explained simpler)?

Thanks in advance

madtyn
  • 1,469
  • 27
  • 55

1 Answers1

1

I guess that you run RemoteControlTest.py directly.

Case A

Python's importer can't go to parent if the module was run directly as script instead of acquiring it going through the package structure.

The Python designers presumably didn't want to let submodules be called directly as scripts so there are no really good solutions for that. Mainly you can

  1. run the script with python -m command.simpleremote.RemoteControlTest when src is either current directory or in PYTHONPATH or
  2. use a testing framework which does the calling for you.

A more detailed discussion of possible solutions can be found at Relative imports in Python 3.

Case B

A consequence of the direct start is that "src/command/simpleremote" is in the module search path. "src" seems also to be added to the path but after "src/command/simpleremote".

Before case B on from command... the import mechanism didn't find matching "command" in "src/command/simpleremote" and continued looking in "src" where the command package was found -> success.

In case B it findscommand.py in "src/command/simpleremote" which isn't a package -> error.

Michael Butscher
  • 10,028
  • 4
  • 24
  • 25
  • About A: for relative imports and going through the package structure is not enough putting the __init__.py files in each folder? About B: I'm reading about the module search path link to test this point. PyCharm does a sys.path.exy¡tend which I think may be related. – madtyn Sep 17 '17 at 00:52
  • @madtyn About A: No, for a directly started script e.g. the `__package__` attribute is empty. If module was loaded through the packages, `__package__` contains the package name. – Michael Butscher Sep 17 '17 at 01:43
  • I've been reading these days. Python docs and PEP366, which reccomends a two-line work around. I've tried with several values in `__package__` but always shows `Parent module 'package.value' not loaded, cannot perform relative import ` – madtyn Sep 20 '17 at 21:15
  • @madtyn I also read about it, especially https://stackoverflow.com/questions/16981921/relative-imports-in-python-3 . Unfortunately it seems that it is just not desired by Python's designers to call submodules as scripts. The only thing I found is (1) to run the script with `python -m command.simpleremote.RemoteControlTest` when `src` is either current directory or in `PYTHONPATH` or (2) to use a testing framework which does the calling for you. – Michael Butscher Sep 21 '17 at 23:00
  • I'm afraid you're right. I'm coping with this as I find more comfortable. You may answer publicly for anyone who may read here and I will accept the answer and only change the accepted answer if I find something better in the future. Thanks. – madtyn Sep 22 '17 at 00:56
  • @madtyn I have edited the answer to elaborate it more and added the "solutions"/workarounds. – Michael Butscher Sep 22 '17 at 12:27