How do I get the location of the entry module in python? - python

I know I can use __file__ and os.path.* to figure out the path of the current module. What about the entry module?
I'm trying to load an ini file into ConfigParser from a module based on the hostname, but I want it to be relative to the entry assembly, not necessarily the working directory. Is this possible?

If by entry module you mean the program's entrypoint this is usually sys.argv[0]. So you could do something like this:
Example:
import os
import sys
def main():
basedir = os.path.dirname(sys.argv[0])
print "base directory: {0:s}".format(basedir)
if __name__ == "__main__":
main()

Related

Is there a better way to set sys.path.append in python project?

I have a simple python project with the following directory structure:
sample-project/
main/
config/
config.yaml
docker/
Dockerfile
tests/
__init__.py
sometests.py
config.py
run.py
__init__.py
requirements.txt
README.rst
In the config.py file, I have:
import yaml
class Config:
def __init__(self):
with open("config/config.yaml", 'r') as ymlfile:
cfg = yaml.load(ymlfile)
self.host = cfg["mysql"]["host"]
self.database = cfg["mysql"]["database"]
self.user = cfg["mysql"]["user"]
self.password = cfg["mysql"]["password"]
my_config = Config()
In the run.py file, I have an import statement as follows:
from main.config import my_config
when I run using command line: python3.5 run.py, I get the error:
from main.config import my_config
ImportError: No module named 'main'
But when I add this in run.py imports it works:
import sys
sys.path.append('/home/tomas/sample-project/')
Is there any better way to make it work rather than give absolute path or any other way? Please give some example :)
Generally, never ever touch sys.path from within your program.
Since main/ (not the best name) contains an __init__.py, we'll consider it a package, so let's make it runnable as one. This way imports will be considered correctly.
Add main/__main__.py with e.g.
from main.run import run
if __name__ == "__main__":
run()
Then run your app with
python -m main
instead of
python main/main.py
when mentioning below
from main.config import my_config
keep dir path also
eg:
from sample-project/main.config import my_config
assuming you are executing from /home/tomas
Although I share AKX's sentiment that editing your path is something to avoid I would generally suggest the following method:
import sys
from os.path import dirname
abs_dir = dirname(__file__)
sys.path.append(abs_dir)
The __file__ variable provides access to the location of the file in which it is accessed. This allows you to get an absolute path to any file in your project.

Python get file relative to command line user

I'm writing a python script that will be aliased and run from various directories like this:
# working
python myScript.py file.txt
or:
# not working
python script/myScript.py other_file.txt
I'm referencing the file input like this:
file = sys.argv[1]
How can I have the script look for the file based on the command line users location instead of relative to the script's location?
Try this:
import os
print(os.getcwd())
This will give you the current working directory(cwd). And using other functions like os.path.join, you can achieve what you want.
Full example:
import os
import sys
def main():
if len(sys.argv) < 2:
print('Not enough arguments.')
sys.exit(1)
print('Current working directory: %s' % os.getcwd())
print('What you want: %s' % os.path.join(os.getcwd(), sys.argv[1]))
if __name__ == '__main__':
main()
Try using it.

Using exec on a file in a different directory causes module import errors

I have 3 very simple scripts. The structure looks like this:
test.py
test_problem_folder
test_problem_1.py
test_problem_2.py
test.py:
import os
if __name__ == "__main__":
filename = "./test_problem_folder/test_problem_1.py"
exec(compile(open(filename, "rb").read(), filename, 'exec'), globals(), locals())
test_problem_folder/test_problem_1.py:
import test_problem_2
test_problem_2.test()
test_problem_folder/test_problem_2.py:
def test():
print("Hello")
If I try to run test.py, I get the error:
ModuleNotFoundError: No module named 'test_problem_2'
If I flatten the folder structure so that test_problem_* is the same directory as test.py, I don't get this problem. I figured the paths must be getting screwed up, so I tried os.chdir() to ./test_problem_folder, but that still gets the same error. What am I doing wrong? My real scenario is more complicated and I need to use exec instead of popen.
I tried your code, if i run python test_problem_1.py under test_problem_folder, everything is working properly. Apparently, the Python path doesnt know anything about test_problem_folder
You can append abs path of test_problem_folder to your python path, then the module can be found, you dont have to have the __init__.py file under test_problem_folder
import os
import sys
if __name__ == "__main__":
sys.path.append("/path/to/.../test_problem_folder")
filename = "./test_problem_folder/test_problem_1.py"
exec(compile(open(filename, "rb").read(), filename, 'exec'), globals(), locals())
Alternatively, you can append the directory of test.py to pythonpath, create __init__.py under test_problem_folder(this makes it as a python package other than directory) and then import test_problem_1 from module test_problem_folder
import os
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import test_problem_folder.test_problem_1 as problem1
if __name__ == "__main__":
pass

ImportError: No module named modules.TestModule

I have been stuck in this issue for a long time, my project structure goes like this,
import web
import sys
sys.path.append("...")
import modules.TestModule as TM
urls = (
'/testmethod(.*)', 'TestMethod'
)
app = web.application(urls, globals())
class TestMethod(web.storage):
def GET(self, r):
return TM.get_func(web.input().string)
if __name__ == "__main__":
app.run()
When I execute the TestService.py it says "ImportError: No module named modules.TestModule Python"
This is a sample made to test the module.
What is the entry point for your program? Usually the entry point for a program will be at the root of the project. Since it is at the root, all the modules within the root will be importable.
But in your case entry point itself is a level inside the root level.
The best way should be have a loader file (main.py) at root level and rest can be in packages. The other non-recommended way is to append root directory path in sys.path before importing any package.
Add below code before you import your package.
import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, '..'))
if rootDir not in sys.path: # add parent dir to paths
sys.path.append(rootDir)
I have modified yut testscript.py code as below. Please try it out.
import web
import sys
import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, '..'))
if rootDir not in sys.path: # add parent dir to paths
sys.path.append(rootDir)
import modules.TestModule as TM
urls = (
'/testmethod(.*)', 'TestMethod'
)
app = web.application(urls, globals())
class TestMethod(web.storage):
def GET(self, r):
return TM.get_func(web.input().string)
if __name__ == "__main__":
app.run()
If the directory 'DiginQ' is on your python sys.path, I believe your import statement should work fine.
There are many ways to get the directory 'DiginQ' on your python path.
Maybe the easiest (but not the best) is to add a statement like this before you import modules.Testmodule:
import sys
sys.path.extend([r'C:\Python27\DiginQ'])
Based on your post it looks like the path is C:\Python27\DiginQ but if that's not correct just use the right path.
If you google how to set python path you will find many hits. Most important thing is your path has to include the directory above the directory with the package you are attempting to import. In your case, that means DiginQ.

Reload all modules in a directory

I need to reload all the python modules within a specified directory.
I've tried something like this:
import sys, os
import_folder = "C:\\myFolder"
sys.path.insert( 0 , import_folder )
for dir in os.listdir(import_folder):
name = os.path.splitext(dir)[0]
ext = os.path.splitext(dir)[1]
if ext == ".py":
import( eval(name) )
reload( eval(name) )
Anyone know how to do this correctly?
import os # we use os.path.join, os.path.basename
import sys # we use sys.path
import glob # we use glob.glob
import importlib # we use importlib.import_module
import_folder = 'C:\\myFolder'
sys.path.append(import_folder) # this tells python to look in `import_folder` for imports
for src_file in glob.glob(os.path.join(import_folder, '*.py')):
name = os.path.basename(src_file)[:-3]
importlib.import_module(name)
reload(sys.modules[name])
importlib.import_module(name)
There is the code. Now to the semantics of the whole thing: using importlib makes this a little bit more normal, but it still promotes some bugs. You can see that this breaks for source files in a subdirectory. What you should probably do is: import the package, (import the whole folder), and use the . operator like so:
import sys # we use sys.path
sys.path.append('C:\\')
import myFolder
...
myFolder.func1(foo)
myFolder.val
bar = myFolder.Class1()
Perhaps you should take a look at the documentation for modules, but don't forget to update the path to include the parent of the folder you want to import.

Categories