Pycharm can't recognize changes in sys.path - python

Some background - I am working on a project, and have been writing tests with PyTest.
The structure of the folders is as following:
src/
...
tests/
helpers/
fixtures.py
functions.py
conftest.py
test1.py
test2.py
...
The helpers sub-folder holds functions and fixtures which should be accessible across all the tests.
In order to do so I added the following to conftest.py:
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers'))
from fixtures import fixture1, fixture2, ....
This actually works, fixtures 1,2,.. are all available across the different tests.
And also:
import functions
works correctly from any file in the tests folder.
But the problem is that PyCharm can't recognize this change, and refer to the modules fixtures, functions as valid, which cancels the auto completion and gives pretty annoying false inspection comments.
If someone knows how to fix it (Notify PyCharm on the update), or maybe give another alternative for how things should be done (that's why I gave all of this context), I will be very grateful.

In the project view on the left, you should right-click on the helpers folder and click on: Mark Directory as->Sources Root, and this should resolve your problem.
After doing that, there will be no need for sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers')) within PyCharm.
But since you already have a working system and trying to get rid of the annoying false error, only marking directory as source root should do the trick just fine.

Related

python import packages that know nothing about my system configuration

I have a project I am working on, let's call it Project, which lives in the directory Project somewhere wholly unknown to me (really it lives both on my local system and on a couple Docker build systems). In that project, I have some source files, source/module1.py and source/module2.py. I also have some example files, some test files, and an init.py So my directory looks something like this:
Project
__init__.py
/source
module1.py
module2.py
/test
testRunner.py
/examples
awesomeExample.py
However, module1 needs some stuff from module2. My naive self thought this could be done by putting an import statement in module1:
import module2
# Do some other interesting stuff
And this works, but only when I am running / importing module 1 from the source directory. If I am, for example, running some unit tests in another directory test/testRunner.py, either from the test directory or in the main Project directory, the import will fail. Same with trying to use it when running an example in the examples directory.
So here is my problem: in general, I don't know where the calling script lives. It might be in the examples directory, it might be in the test directory, or it might be in the main Project directory (for example when trying to import stuff with an init.py). How do I ensure that module1 can always import module2 in each of these scenarios?
I am not looking for a solution like "add all those directories to your python path". Initially I just added Project to my python path on my local machine, and then did all my imports relative to that (import Project.source.module2), but this (predictably) caused my builds to fail on the Docker instances. I don't just want this to work on my local machine, but also on the Docker instances I'm using to build and test this software, and on any user's machine that subsequently installs it (i.e. by doing a pip install Project. What is the most robust way to make sure this dependency is satisfied? How can I make sure module1 can import module2 regardless of where module1 itself is imported from? Any python 3.x.x solution is welcome.
I figured out a way to do it (credit here) - it's a little inelegant, but extremely robust. Works on my local machine independent of whether using an import statement or running a script directly, as well as my build servers which use github actions and Travis CI.
Basically, I added a file in the source directory, called context.py with the following contents:
import os
import sys
fileLocation = os.path.dirname(os.path.abspath(__file__))
sourceLocation = os.path.abspath(os.path.join(fileLocation, '..', 'source/'))
sys.path.insert(0, sourceLocation)
This finds out the current file being executed from python, and then uses that to add to the python path. And then in my module1.py file, at the top I have:
import context
import module2
Now, whenever module1 is imported, it successfully imports module2. More elegant answers or comments on why this works and in which cases it might fail are appreciated.

Python finding some, not all custom packages

I have a project with the following file structure:
root/
run.py
bot/
__init__.py
my_discord_bot.py
dice/
__init__.py
dice.py
# dice files
help/
__init__.py
help.py
# help files
parser/
__init__.py
parser.py
# other parser files
The program is run from within the root directory by calling python run.py. run.py imports bot.my_discord_bot and then makes use of a class defined there.
The file bot/my_discord_bot.py has the following import statements:
import dice.dice as d
import help.help as h
import parser.parser as p
On Linux, all three import statements execute correctly. On Windows, the first two seem to execute fine, but then on the third I'm told:
ImportError: No module named 'parser.parser'; 'parser' is not a package
Why does it break on the third import statement, and why does it only break on Windows?
Edit: clarifies how the program is run
Make sure that your parser is not shadowing a built-in or third-party package/module/library.
I am not 100% sure about the specifics of how this name conflict would be resolved, but it seems like you can potentially a). have your module overridden by the existing module (which seems like it might be happening in your Windows case), or b). override the existing module, which could cause bugs down the road. It seems like b is what commonly trips people up.
If you think this might be happening with one of your modules (which seems fairly likely with a name like parser), try renaming your module.
See this very nice article for more details and more common Python "import traps".
Put run.py outside root folder, so you'll have run.py next to root folder, then create __init__.py inside root folder, and change imports to:
import root.parser.parser as p
Or just rename your parser module.
Anyway you should be careful with naming, because you can simply mess your own stuff someday.

Python: Nosetests with multiple files

This is a broad question because no one seems to have found a solution to it as yet so I think asking to see a working example might prove more useful. So here goes:
Has anyone run a nosetests on a python project using imports of multiple files/packages?
What I mean is, do you have a directory listing such as:
project/
|
|____app/
|___main.py
|___2ndFile.py
|___3rdFile.py
|____tests/
|____main_tests.py
Where your main.py imports multiple files and you perform a nosetests from the project file of utilizing a test script in the main_tests.py file? If so please can you screen shot your import section both of all your main files and your main_tests.py file?
This seems to be a major issue in nosetests, with no apparent solution:
Nosetests Import Error
A test running with nosetests fails with ImportError, but works with python command
https://github.com/nose-devs/nose/issues/978
https://github.com/nose-devs/nose/issues/964
You can't have python modules starting with a digit, so 2ndFile.py, 3rdFile.py won't actually work (rename them).
You'll need an __init__.py inside the app directory, for it to be considered a package, so add that (it can be empty file).
You don't need an __init__.py in the tests directory!
The import statements in main_tests.py should look like from app.main import blah
The absolute path of the project directory needs to be in your sys.path. To achieve this, set an environment variable: export PYTHONPATH=/path/to/project
Now running nosetests should work.

How can I make the application package available to the tests, when using py.test?

Suppose I'm writing a test. Obviously it is testing my app, so I need to import the app package somehow into the test script. The directory structure is this:
root/
app/
__init__.py
somemodule.py
tests/
my_test.py
And I run the tests like so:
cd tests
py.test # runs all the tests in the current directory
The question is: How should I import the application modules in my test modules?
In my_test.py, I tried doing from .. import app. This gives me an error Parent module '' not loaded, cannot perform relative import.
What is the standard way to accomplish this?
EDIT: Please note I edited the question to refer specifically to the py.test tool.
You should be able to make it run by properly configuring your py.test.
Add your module to your app/__init__.py the following line
from .somemodule import MyClass # or whatever ur class is called
Create a file called conftest.py in your main folder. You can leave it empty but it is used by py.test to find out the project path. Inside you can run some py.test initialization like adding fixtures.
In your my_test.py you will be able now to call
from app import MyClass
Now from your main folder you can finally:
py.test tests/test.py
This has worked for me. I think py.test has a way to include modules since you are probably not able to achieve the same without it. At least if I did not use py.test I would stick to modifying my PYTHONPATH to point to my application path.
EDIT:
Just to clarify py.test manipylates the sys.path for the testing session to include the root directory. Py.test identify the root path by using the conftest.py file. The root path is then added to the system path and used for testing.
You are indeed able to run:
py.test tests/test.py
and this would also work:
cd..
py.test rootTest/tests/test.py

Python import modules, folder structures

I have been looking for a way to solve this.
I have a python project, and this is the folder structure I want:
/project/main.py
/project/src/models.py
/project/test/tests.py
I want to be able to run the tests by executing the tests.py in terminal. tests.py imports modules in /project/src/ for testing. First I solved this by adding
sys.path.insert(0, '..') in tests.py. But then the paths used in models.py for opening text files had to be relative to the tests.py, etc. Which means the program wouldn't run when excecuted from main.py, cause of the paths.
I also tried with dots when importing modules into tests.py, like from ..src.models import *, but that gave error message saying "Attempted relative import in non-package".
What should I put in the top of tests.py to be able to import the modules from models.py?
The structure you're using is not one I would recommend, but I'm a comparaitive newb to how Python projects are usually structured. I believe this will do what you're after:
1) Place an __init__.py file inside /project, /project/src, and /project/test to make sure they're treated as packages.
2) Place from __future__ import absolute_import at the top of each Python file.
3) Then use relative imports:
test.py:
from ..src import models
main.py:
from .src import models
4) You'll need to start your application differently. Ensure your current directory is the parent of /project (which appears to be the file system root) and run your project this way:
python -m project.main
For my own project, I would definitely put main.py inside src if it's the start point of your application. I might put tests.py in src, too, but if not, I would add /project/src to the test runner's Python path on the command line instead of in code.
I would still use absolute_import regardless. In my experience, it's a very clean solution to module organization, and it's also how things work by default in Python 3.
first put your main.py in the src directory..
in your tests you can do , sys.path.append('the src directory')
if you like to force the execution to be in specific directory regardless from where you are executing the app i suggest you adding
import os
os.chdir('relative path to the src dir')
thisway your program will run in the directory you specified so it will respect the relative paths you have in your code

Categories