Desiring to improve my Python style, I ran a PEP-8 style checker on one of my script and it complained about something I don't know how to fix. The prologue of the script is something like:
#! /bin/env python3
import sys
import os
exe_name = os.path.basename(os.path.realpath(__file__))
bin_dir = os.path.dirname(os.path.realpath(__file__))
inst_dir = os.path.dirname(bin_dir)
sys.path.insert(0, inst_dir+'/path/to/packages')
import mypackage.mymodule
and the style checker complain on the import mymodule line, stating that it should be a top of file. But I obviously can't move it before setting the path where it will be found. Is there a good way to achieve this (mandating an environment variable or a shell wrapper are not what I find better than my current code) while respecting PEP-8 recommendations at the same time?
If you want to avoid path manipulation, you may be able to do so by using the under-known .pth feature.
sys.path should begin with the directory containing the main program either by name or by reference as ''. I assume that the file importing mymodule is not part of mypackage, so that the '' entry is not useful for importing mymodule.
sys.path should end with the site-packages directory for the executing binary. That is the normal place for added packages. If you do not want to move mypackage into site-packages, you can extend the latter 'vitually' by putting a mystuff.pth file in it. It should contain one line: the path to the directory containing mypackage. Call it myprojects. Then mypackage and any other package in myprojects can be imported as if they were in site-packages.
One advantage of .pth files is that you can put identical copies in multiple site-packages directories. For instance, I have multiple projects in F:/python. I have multiple versions of Python installed. So I have put python.pth containing that one line in the site-packages for each.
The best strategy would be to put the sys.path related code in separate file and import it in working code file.
So, I will split above code in two files. One named my_module.py and other named working_module.py
Now my_module will contain below lines
import sys
import os
exe_name = os.path.basename(os.path.realpath(__file__))
bin_dir = os.path.dirname(os.path.realpath(__file__))
inst_dir = os.path.dirname(bin_dir)
sys.path.insert(0, inst_dir+'/path/to/packages')
and working_module will contain
import my_module
import mypackage.mymodule
because we are importing my_module before mypackage, it will execute the path related code before and your package will be available in path.
You can use importlib module (python 3.5) or imp for python 2.7 to load mypackage.mymodule programatically. Both have the same purpose:
mechanisms used to implement the import statement
This question might help you more:
How to import a module given the full path?
https://docs.python.org/3/library/importlib.html#examples
https://docs.python.org/2/library/imp.html
Related
If I have a project with two files main.py and main2.py. In main2.py I do import sys; sys.path.append(path), and I do import main, which main.py will be imported? The one in my project or the one in path (the question poses itself only if there are two main.py obviously)?
Can I force Python to import a specific one?
The one that is in your project.
According to python docs, the import order is:
Built-in python modules. You can see the list in the variable sys.modules
The sys.path entries
The installation-dependent default locations
Your added directory to sys.path, so it depends on the order of entries in sys.path.
You can check the order of imports by running this code:
import sys
print(sys.path)
As you will observe, the first entry of sys.path is your module's directory ("project directory") and the last is the one you appended.
You can force python to use the outer directory by adding it in the beginning of your sys.path:
sys.path.insert(0,path)
Although I would recommend against having same names for the modules as it is often hard to manage.
Another thing to keep in mind is that you can't import relative paths to your sys.path. Check this answer for more information on how to work around that: https://stackoverflow.com/a/35259170/6599912
When two paths contain the same module name, using sys.path.append(path) isn't going to change the priority of the imports, since append puts path at the end of the python path list
I'd use
sys.path.insert(0,path)
so modules from path come first
Example: my module foo.py imports main and there's one main.py in the same directory and one main.py in the sub directory
foo.py
main.py
sub/main.py
in foo.py if I do:
import sys
sys.path.append("sub")
import main
print(main.__file__)
I get the main.py file path of the current directory because sys.path always starts by the main script directory.
So I could remove that directory from sys.path but it's a bad idea because I could need other modules from there.
Now if I use insert instead:
import sys
sys.path.insert(0,"sub")
import main
print(main.__file__)
I get sub/main.py as sub is the first directory python looks into when searching modules.
As per screen print, import shows error in Python 3.7 version, earlier it was working fine in version Python 2.7 and I am using IntelliJ Idea.
If you see, EOC related .py files are in the same folder and have classes which are being called in Main_EOC.py by passing objects which are inter-related. It's amazing to see the red line while importing files from same folder.
Please help me why it's showing such error
"This inspection detects names that should resolve but don't. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Top-level and class-level items are supported better than instance items.`"
Also, if you see the line which have full path, is not showing error
from EOC_Module.eoc.script.config import Config
Please help me if there is a way to add this full path on top of the code or other option.
The behavior of import path search changed between python2 and python3. The import path always includes the directory from which the main module was loaded, but it no longer includes directories from which modules were imported.
You need to change your import statement syntax as follows, if you want to import a module that lives in the same directory as the module in which you do the import:
# old way, import works if the named module is in this module's directory
import x
# new (Python3) way:
from . import x
For the second part: adding a path so all code can import from a certain directory: if that directory is (and will always be) relative to your main: you can add a few lines in the main module to make it available. Something like this:
import sys # if you haven't imported it already
import os.path
home = os.path.dirname(sys.argv[0])
sys.path.append( os.path.join(home, "EOC_Module/eoc/script") )
# now, you can import straight from the script directory
import EOC_Intraction
When using pycharm the root directory for your python executable is the same as the root directory of your project, this means that python will start looking for files in the root directory with this files:
.idea/
EOC_module/
logs/
reports/
sql/
This is the reason of why: from EOC_Module.eoc.script.config import Config works.
If you execute your code from the terminal with: python3 Main_EOC.py (not pycharm) the root directory for your python will be the same as the one containing the file, all the other imports will work but from EOC_Module.eoc.script.config import Config not.
So you need to make your imports from project directory if you are using pycharm.
I have the following directory structure:
root
/src
file1.py
file2.py
/libs
__init__.py
package.so
I wish to import package.so within file1.py.
I've tried the following import statements to no avail:
from .libs.package import func
from libs.package import func
from .libs import package
from libs import package
I want to avoid having to set PYTHONPATH / sys.path.
Is there a simple way to do this? I assume the issue is due to the package being a shared object and not just a Python file - I don't have access to the source code for it.
Thanks,
Adam
If you are intent on not using sys.path.append to import the file, I was able to do it using the following snippet in file1.py:
import imp
import os
file = os.sep+os.path.join(*os.path.realpath(__file__).split(os.sep)[:-1]+['..','libs','package.so'])
package = imp.load_source('root.libs.package', file)
print package.a()
Which prints <root.libs.package.a instance at 0x101845518>. Note that my package.so file is just a python file that defines a dummy class a so that I could test importing it. From what I have read, I believe that replacing imp.load_source with imp.load_dynamic may be what you want.
Breaking it down:
os.path.realpath(__file__).split(os.sep)[:-1] gets you the path to the directory the currently-running script is in, as a list of strings.
+['..','libs','package.so'] concatenates a list containing the parent directory (..), the libs directory, and your filename to that list, so that os.path.join will build the full path to the package.so file.
os.path.join(*[that list]) unpacks the list elements into arguments to os.path.join, and joins the strings with os.sep. I also add a leading os.sep since it is an absolute path.
imp.load_source returns a module with the name root.libs.package loaded from the file path.
This source was useful for writing this answer, and here are the docs for the imp module, which you might also find useful.
You can use relative import as described in python docs:-here
Also it only works well for directories under package which will not ask for beyond import. If so will happen then this error will occur:-
valueError: attempted relative import beyond top-level package
You can refer this question too for same:-
value error for relative import
In order to import a file from the directory above, you should use ..
In your case, you need to import using one of the following lines:
from ..libs.package import func
from ..libs import package
from root.libs.package import func
from root.libs import package
Keep in mind that according to the python documentation, using a full path instead of .. is preferred, thus from root.libs is a better choice.
Regarding not being able to traverse up: If your main script is file1.py you should start it from the directory containing root like so:
python -m root.src.file1 args
This way python will treat your root as a package.
Last but not least: As you're using Python 2, you'll need to put __init__.py in every directory, including src and root.
I am running into pathing issues with some scripts that I wrote to test parsers I've written. It would appear that Python (27 and 3) both do not act like IronPython when it comes to using the current working directory as part of sys.path. As a result my inner package pathings do not work.
This is my package layout.
MyPackage
__init__.py
_test
__init__.py
common.py
parsers
__init__.py
_test
my_test.py
I am attempting to call the scripts from within the MyPackage directory.
Command Line Statements:
python ./parsers/_test/my_test.py
ipy ./parsers/_test/my_test.py
The import statement located in my my_test.py file is.
from _test.common import TestClass
In the python scenario I get ONLY the MyPackage/parsers/_test directory appended to sys.path so as a result MyPackage cannot be found. In the IronPython scenario both the MyPackage/parsers/_test directory AND MyPackage/ is in sys.path. I could get the same bad reference if I called the test from within the _test directory (it would have no idea about MyPackage). Even if i consolidated the test files into the _test directory I would still have this pathing issue.
I should note, that I just tested and if i did something like
import sys
import os
sys.path.append(os.getcwd())
It will load correctly. But I would have to do this for every single file.
Is there anyway to fix this kind of pathing issue without doing the following.
A. Appending a relative pathing for sys.path in every file.
B. Appending PATH to include MyPackage (I do not want my package as part of the python "global" system pathing)
Thanks alot for any tips!
Two options spring to mind:
Use the environment variable PYTHONPATH and include .
Add it to your sys.path at the beginning of your program
import sys.path
sys.path.append('.')
If you need to import a relative path dynamically you can always do something like
import somemodule
import sys
dirname = os.path.dirname(os.path.abspath(somemodule.__file__))
sys.path.append(dirname)
I created a project in python, and I'm curious about how packages work in python.
Here is my directory layout:
top-level dir
\ tests
__init__.py
\ examples
__init__.py
example.py
module.py
How would I go about including module.py in my example.py module. I know I could set PYTHONPATH to the top-level directory, but that doesn't seem like a good solution. This is how pydev gets around this problem, but I'd like a solution that didn't require updating environment variables.
I could put something at the top of the example.py to update sys.path like this:
from os import path
import sys
sys.path.append( path.dirname(path.abspath(path.dirname(__file__))) )
I don't think this is an appropriate solution either.
I feel like I'm missing some basic part of python packages. I'm testing this on python 2.6. If any further clarification is needed, please let me know.
Python packages are very simple: a package is any directory under any entry in sys.path that has an __init__.py file. However, a module is only considered to be IN a package if it is imported via a relative import such as import package.module or from package import module. Note that this means that in general, someone must set up sys.path to contain the directories above any package you want to be importable, whether via PYTHONPATH or otherwise.
The primary wrinkle is that main modules (those run directly from the command line) are always __main__ no matter their location. They therefore have to do absolute imports, and either rely on PYTHONPATH being set up, or munge sys.path themselves.
In your case, I'd recommend either having a small Python script that runs your examples after setting up the correct path. Say you put it in the top level directory:
#!/usr/bin/env python
import sys
import os.path
sys.path.append(os.path.dirname(__file__))
example = __import__("examples", globals(), locals(), sys.argv[1])
Then your example can do "import module".
Alternately, if module.py is meant to also be in a package, add the PARENT of your "top level directory" to sys.path and then use the from .. import module syntax in your example modules. Also, change the first parameter to the __import__ in the wrapper to "tldname.examples" where tldname is the name of your top-level directory.
If you don't want to rely on an "example runner" module, each example needs sys.path boilerplate, or you need a PYTHONPATH setting. Sorry.
http://docs.python.org/tutorial/modules.html#intra-package-references
from .. import module