Using sys.path.insert without explicit absolute path - python

New to using sys.path to enable module import from another directory, so I'm sure this is a noob question but:
Is it possible to not have to use the full/explicit or absolute file path when using sys.path to access a Python script in another directory, but instead, to only provide the directory path that's local to the module file structure?
Currently, I have the following directory structure:
MyModule/
NAME/
__init__.py
bin/
userinfo.py
__init__.py
docs/
setup.py
tests/
NAME_tests.py
__init__.py
Within the setup.py file, I have it import the userinfo.py script, which just asks the users for some info during installation. In the setup.py file, the lines that call the userinfo.py script look like this:
import sys
sys.path.insert(0, '/Users/malvin/PythonDev/projects/MyModule/bin')
import userinfo
This works fine, because I know the entire file path where the userinfo.py file is, but obviously this doesn't work for someone who's trying to install the module because (obviously) there's no way to anticipate the file path on the user's system.
My question: Is there a method wherein I can import the userinfo.py file (which lives in the /bin folder) without having the entire system file path (that goes all the way up to /Users) ? In other words, I'd like to just have something like:
import sys
sys.path.insert(0, 'MyModule/bin')
import userinfo
But I know this doesn't work.

You can use the dot (./) notation for the current working directory to establish a relative path.
For example:
(syspathinsert)macbook:syspathinsert joeyoung$ pwd
/Users/joeyoung/web/stackoverflow/syspathinsert
(syspathinsert)macbook:syspathinsert joeyoung$ tree
.
├── MyModule.py
└── bin
├── userinfo.py
└── userinfo.pyc
1 directory, 3 files
(syspathinsert)macbook:syspathinsert joeyoung$ cat ./bin/userinfo.py
def say_hello():
print "hello there"
(syspathinsert)macbook:syspathinsert joeyoung$ cat MyModule.py
import sys
sys.path.insert(0, './bin')
import userinfo
userinfo.say_hello()
(syspathinsert)macbook:syspathinsert joeyoung$ python MyModule.py
hello there

Related

Python: How to import file from subdirectory of parent of parentdirectory?

How can I import file from subdirectory of parent of parentdirectory? I know that this doesn't make sense so let me explain. I want to import the file test.py into the file example.py.
Here is the directory structure:
directory1
+-- directory2
+-- test.py
+-- directory3
+-- directory4
+-- example.py
Add __init__.py file inside each directory, parent directory and subdirectories
parent/
__init__.py
package_1/
__init__.py
package_2/
__init__.py
package_3/
__init__.py
from parent.package_1 import ....
If doesn't work, try to configure PYTHONPATH, in case of IDE like PyCharm, there's a flag (checkbox) in run/debug configuration
or you might add module to the default path using sys.path.insert(...)
import sys
sys.path.insert(0, module_full_path)
You could check path, using print(sys.path)
Just make sure folder also contains an __init__.py, this allows it to be included as a package
You have serveral options:
Like the comment said you can use a full path:
/home/User/directory1/directory2/test.py
Map that file to a variable and use it. (Watch out with using python built in names like test)
use os.chdir and change your dir to the root of you directory1 and call directory2/test.py
make an init.py and use it as a package with the from [directory] import [module]
Here you go =^..^=
from pathlib import Path
import sys
path = Path(__file__).parents
sys.path.append(str(path[2] / 'directory2'))
import test

Python - ModuleNotFoundError: No module named

I'm new in Python and I'm having the following error with this simple example:
This is my project structure:
python_project
.
├── lib
│   ├── __init__.py
│   └── my_custom_lib.py
└── src
├── __init__.py
└── main.py
And this is the error when I execute the src/main.py file:
☁ python_project python src/main.py
Traceback (most recent call last):
File "src/main.py", line 3, in <module>
from lib import my_custom_lib
ImportError: No module named lib
If I move the main.py file to the root and then I execute this file again, works... but is not working inside src/ directory
This is my main.py:
from lib import my_custom_lib
def do_something(message):
my_custom_lib.show(message)
do_something('Hello World!')
Note: When I execute the same code from Pycharm is working fine, but not from my terminal.
Your PYTHONPATH is set to the parent directory of the executed script. So if the executed script is inside a directory src, it will never be able to find the sister directory lib because it isn't within the path. There's a few choices;
Just move lib/ into src/ if it belongs to your code. If it's an external package, it should be pip installed.
Have a top-level script outside of src/ that imports and runs src.main. This will add the top-level directory to python path.
In src/main.py modify sys.path to include the top-level directory. This is usually frowned upon.
Invoke src/main.py as a module with python -m src.main which will add the top-level directory to the python path. Kind of annoying to type, plus you'll need to change all your imports.
If I may add to MarkM's answer, if you wanted to keep your current directory structure and still make it work, you could add a setup.py in your root dir, where you can use setuptools to create a package you could install.
If your file had something along the lines of:
# setup.py
from setuptools import find_packages, setup
setup(
name='foo',
version=`1.0.0`,
packages=find_packages(),
entrypoints={
'console_scripts': [
'foo=src.main:main',
],
},
)
And then you do pip install [--user] -e path/to/directory you'll get an "editable package" which will effectively a symlink to the package in your development directory, so any changes you make will not require a reinstall (unless of course you rejig package structure or add/remove/edit entry points).
This does assume your src/main.py has a main function.
You'll also need __init__.py files in your "package" directories, even in Python3, as otherwise Python assumes these are namespace packages (Won't go into detail) and the find_packages() call won't find them.
This will also allow your relative imports to work. Absolute imports will only work when invoking the script from your entry point but not when calling the script directly in your development directory.
You should have your main.py script above all python packages in your directory structure. Try to update your project to the following structure:
.
|__ main.py
|__ lib
| |__ __init__.py
| |__ your_custom_lib.py
|__ another_python_package
|__ __init__.py
|__ another_python_scripts
After that, python main.py in your project directory will work.
You are using the from a import b incorrectly. it should look like this:
import lib.my_custom_lib
The other method is used to import certain methods, functions, and classes from a module, not the module itself. To import a specific function from the my_custom_lib module it would look like this:
from lib.my_custom_lib import foo
Try using a relative import instead:
from ..lib import my_custom_lib
in my case in visual code
actual error in line 1
I didn't imported _typeshed module but by default it was their
so delete that module if you found in line 1
MarkM's answer is still excellent; I'm using PyPi now. I attempted to disambiguate what he was saying about "current directory". In case my confusion was an intended feature, here is how I did it.
vagrant#testrunner:~/pypath$ tree
.
├── proga
│   └── script1.py
└── progb
└── script1.py
script1.py is the same in both directories:
#!/usr/bin/env python3
import sys
print(sys.path)
Where I run it from makes no difference, PYTHONPATH prepends the directory containing the script I specify:
vagrant#testrunner:~/pypath/proga$ ./script1.py
['/home/vagrant/pypath/proga', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/home/vagrant/.local/lib/python3.8/site-packages', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']
vagrant#testrunner:~/pypath/proga$ ../progb/script1.py
['/home/vagrant/pypath/progb', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/home/vagrant/.local/lib/python3.8/site-packages', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']
For me importing with explicit paths works best in such situations. If you need to go up in the tree use '..'. The plumbing is a bit cumbersome, but it always works.
path = os.path.abspath(os.path.join(pathlib.Path(__file__).parent.absolute(), '..', 'subdir', 'myFile.py'))
loader = importlib.machinery.SourceFileLoader('myFile', path)
spec = importlib.util.spec_from_loader('myFile', loader)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# now use the module:
module.myMethod()
myClassInstance = module.myClass()

Python import: resolve conflict between current directory and external library

The 101'th times things like these are asked, but still I didn't find any solution for this:
I have 3 python files:
main.py uses functions from math.py and site.py
site.py uses functions from math.py
math.py works independently
Each of these files can contain a main function that's executed if __ name __ == __ main __ and thus the command "python3 file.py" should be able to handle all imports for 'file' = main/site/math.
A second issue is that the filenames site and math are also names of standard libraries.
Besides, the program should be portable in the sense that imports should not contain absolute paths to the other python files.
What are the import statements needed?
Try 1)
All files are in the same directory
main.py
import site, math
site.py
import math
Problem: standard libraries are imported instead.
Try 2)
site.py and mathtools.py (renaming of math.py for this example) are in a subdirectory 'include' of the directory containing main.py
main.py
import include.site, include.mathtools
site.py
import mathtools
Problem: while site.py can be runned without problems, the statement 'import mathtools' leads to an unknown module when main.py is runned.
Try 3)
site.py and math.py are in a subdirectory 'include' of the directory containing main.py
main.py
import sys, os
sys.path.append(os.path.abspath("./include"))
import include.site, include.math
site.py
import math
Problem: while the issue in try 2 is resolved, there's still a confict since math is confused with the standard library.
My ideal solution:
Is there a simple way to state "import from a file in the same directory as the python file which contains this import statement, not from the standard library"
Thanks you on beforehand!
The easiest solution here is to not shadow built in modules with custom ones, and use absolute paths but regardless this might help you out with some more information:
Python will execute an imported module's function from the directory it is called from.
For example, a function called output_file is in the script test1.py in the directory tests/test1.py and will output a text file called test1.txt when run. If you run that function from the script test1.py directly, it will output the text file to /tests/test1.txt. If you were to then import test1.py into a script in the root directory and call the function, it will output the file to the root directory instead.
**Directory path for visual**
├── main.py
├── (if imported and run from main, "test1.txt" is here)
├── tests
│ ├── test1.py
│ ├── (if run from test1 directly, "test1.txt" is here)
All the import statements are executed like you would be importing from the main script (meaning for attempt 3 above, it will import the builtin math.py cause site.py gets run as if it was from the root directory, and doesn't have the absolute path to include/math.py.)

How to import multiple files via one symlink in python?

I have a script script.py and multiple modules in a different directory that all depend on each other. For simplicity, we just look at two of them, module1.py and module2.py, where the first imports the latter. All these should be used by script.py.
Now I added a symlink to module1.py into the directory of script.py, so my directory tree looks like this:
.
├── mymodules
│   ├── module1.py
│   └── module2.py
└── myscript
├── module1.py -> ../mymodules/module1.py
└── script.py
Just running script.py now doesn't work, because PYTHONPATH does not contain the mymodules directory, and module1 is therefore unable to import module2. Now, there is an easy workaround for this; Appending the path of module1.py to PYTHONPATH:
sys.path.append(os.path.abspath(os.path.join(os.path.realpath(__file__),os.path.pardir)))
And this is where problems emerge: This works, but only once!
The first run works fine, all modules are imported without any problems.
But every subsequent execution of $ ./script.py fails with the exception ImportError: no module named module2 and sys.path contains the directory of the symlink, not the file! Why? And how do I fix this?
All of the code:
I thought you could need this if you wanted to try this yourself.
myscript/script.py:
#!/usr/bin/env python
import module1
mymodules/module1.py
#!/usr/bin/env python
import sys, os
#append directory of this file to PYTHONPATH
sys.path.append(os.path.abspath(os.path.join(os.path.realpath(__file__),os.path.pardir)))
#print for control reasons
print sys.path
import module2
mymodules/module2.py
#!/usr/bin/env python
print "import successful!"
Simply, append the real dirname to sys.path
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
This output is
import successful
However, after symbolic module1.py is compiled to module1.pyc, modeul1.pyc will be located at myscript. The above code does not work.
Solution
So solution is to modify mymodules/module1.py as
import os
import sys
srcfile = __file__
if srcfile.endswith('.pyc'):
srcfile = srcfile[:-1] # pyc to py
sys.path.append(os.path.dirname(os.path.realpath(srcfile)))
import module2

Importing from a file in previous path in python

I have this file structure:
.
test/db_test.py
models/user.py
I want to import user.py in db_test.py, for example i try it:
from ..models.user import User
but have this error:
SystemError: Parent module '' not loaded, cannot perform relative import
How can do this work?
all path have __init__.py file
i don't want to use appending in sys.path
thank for your answers
Have you tried running the script as a package? Try running the following from the directory containing your package root directory:
python -m your_package_name.test.db_test
My test that this worked for was:
your_package_name/
__init__.py
test/
__init__.py
db_test.py
models/
__init__.py
user.py
Where "db_test.py" contained:
from ..models.user import User
So I ran that command from the parent directory of "your_package_name".

Categories

Resources