Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How PyCharm imports differently than system command prompt (Windows)

I am having a problem running my script in a cmd prompt despite it working in PyCharm. I have a folder structure as such:

MyCode # PyCharm project folder
  /UsefulFunctions
    /Messaging
      /Texter.py
  /DiscordBot
    /DiscordBot.py

Within DiscordBot.py I have an import

from UsefulFunctions.Messaging import Texter

This works when I run it from PyCharm without a problem. However when I try to run from a command prompt located at the DiscordBot level it errors with:

ImportError: No module named 'UsefulFunctions'

So naturally I thought it meant that the UsefulFunctions folder was not on my path. Therefore, I went into my environment variables and added it to my PATH variable (as well as the MyCode folder for good measure). Still it encountered this error. I browsed some posts on here regarding imports (mainly Importing files from different folder) and they recommend doing something like:

import sys
sys.path.insert(0, '/path/to/application/app/folder')
import file

Or adding __init__.py files to each folder in order to get them to register as packages. I went ahead and added __init__ files to each folder and subfolder I was trying to import from, but still could not run from the command prompt...I ommitted the sys.path.insert() solution because I see no benefit from this after already explicitly adding it to my PATH variable. Another solution was to add "." before the import because supposedly otherwise it is only searching python's PATH. I attempted this as:

from .UsefulFunctions.Messaging import Texter

ImportError: attempted relative import with no known parent package

And this error shows on PyCharm now as well... I don't get why my initial script would work without a hitch on PyCharm, but the same program cannot seem to find my import when run from a prompt. Can somebody please explain the difference between PyCharm running the program and my prompt? Why will this not work despite having __init__.py files and having added MyCode and UsefulFunctions to my PATH variable on Windows?

like image 221
Reedinationer Avatar asked Mar 02 '19 06:03

Reedinationer


2 Answers

From [Python 3.Docs]: Command line and environment - PYTHONPATH:

Augment the default search path for module files. The format is the same as the shell’s PATH: one or more directory pathnames separated by os.pathsep (e.g. colons on Unix or semicolons on Windows). Non-existent directories are silently ignored.

You can also find more details on [SO]: Strange error while using Pycharm to debug PyQt gui (@CristiFati's answer).

So, in order for Python to be able to load a module (package) without specifying its path, the path must be present in %PYTHONPATH% environment variable.

You mentioned %PATH% several times in the question but it's %PYTHONPATH% (MyCode must be added to it).

PyCharm does that because of (any of) the 2 checkboxes in the image below:

Img0

If you want to get things working from cmdline, yo have to do the same thing there as well:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q054955891\DiscordBot]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> set py
Environment variable py not defined

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" DiscordBot.py
Traceback (most recent call last):
  File "DiscordBot.py", line 1, in <module>
    from UsefulFunctions.Messaging import Texter
ModuleNotFoundError: No module named 'UsefulFunctions'

[prompt]> set PYTHONPATH=e:\Work\Dev\StackOverflow\q054955891

[prompt]> set py
PYTHONPATH=e:\Work\Dev\StackOverflow\q054955891

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" DiscordBot.py
e:\Work\Dev\StackOverflow\q054955891\UsefulFunctions\Messaging\Texter.py imported

As a side note, I personally hate names that start with My (e.g. MyCode). Try finding a more useful name (e.g. TestBotProject, or smth similar) :).

like image 177
CristiFati Avatar answered Nov 15 '22 22:11

CristiFati


Python uses the system variable PYTHONPATH, among other things, to decide what to import.
From the docs:

When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

  • The directory containing the input script (or the current directory when no file is specified).
  • PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  • The installation-dependent default.

The reason PyCharm magically imports the module when you run the script is because of the Project Structure -> Content Root value. It points to your project directory, by default.

like image 43
edd Avatar answered Nov 15 '22 22:11

edd