I want to detect whether module has changed. Now, using inotify is simple, you just need to know the directory you want to get notifications from.
How do I retrieve
I'd like to contribute with one common scenario (in Python 3) and explore a few approaches to it.
The built-in function open() accepts either relative or absolute path as its first argument. The relative path is treated as relative to the current working directory though so it is recommended to pass the absolute path to the file.
Simply said, if you run a script file with the following code, it is not guaranteed that the example.txt
file will be created in the same directory where the script file is located:
with open('example.txt', 'w'):
pass
To fix this code we need to get the path to the script and make it absolute. To ensure the path to be absolute we simply use the os.path.realpath() function. To get the path to the script there are several common functions that return various path results:
os.getcwd()
os.path.realpath('example.txt')
sys.argv[0]
__file__
Both functions os.getcwd() and os.path.realpath() return path results based on the current working directory. Generally not what we want. The first element of the sys.argv list is the path of the root script (the script you run) regardless of whether you call the list in the root script itself or in any of its modules. It might come handy in some situations. The __file__ variable contains path of the module from which it has been called.
The following code correctly creates a file example.txt
in the same directory where the script is located:
filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')
with open(filepath, 'w'):
pass
I will try tackling a few variations on this question as well:
(Some of these questions have been asked on SO, but have been closed as duplicates and redirected here.)
__file__
For a module that you have imported:
import something
something.__file__
will return the absolute path of the module. However, given the folowing script foo.py:
#foo.py
print '__file__', __file__
Calling it with 'python foo.py' Will return simply 'foo.py'. If you add a shebang:
#!/usr/bin/python
#foo.py
print '__file__', __file__
and call it using ./foo.py, it will return './foo.py'. Calling it from a different directory, (eg put foo.py in directory bar), then calling either
python bar/foo.py
or adding a shebang and executing the file directly:
bar/foo.py
will return 'bar/foo.py' (the relative path).
Now going from there to get the directory, os.path.dirname(__file__)
can also be tricky. At least on my system, it returns an empty string if you call it from the same directory as the file. ex.
# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)
will output:
__file__ is: foo.py
os.path.dirname(__file__) is:
In other words, it returns an empty string, so this does not seem reliable if you want to use it for the current file (as opposed to the file of an imported module). To get around this, you can wrap it in a call to abspath:
# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))
which outputs something like:
os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar
Note that abspath() does NOT resolve symlinks. If you want to do this, use realpath() instead. For example, making a symlink file_import_testing_link pointing to file_import_testing.py, with the following content:
import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)
executing will print absolute paths something like:
abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py
file_import_testing_link -> file_import_testing.py
@SummerBreeze mentions using the inspect module.
This seems to work well, and is quite concise, for imported modules:
import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)
obediently returns the absolute path. For finding the path of the currently executing script:
inspect.getfile(inspect.currentframe())
(thanks @jbochi)
When you import a module, yo have access to plenty of information. Check out dir(a_module)
. As for the path, there is a dunder for that: a_module.__path__
. You can also just print the module itself.
>>> import a_module
>>> print(dir(a_module))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
>>> print(a_module.__path__)
['/.../.../a_module']
>>> print(a_module)
<module 'a_module' from '/.../.../a_module/__init__.py'>
Here is a quick bash script in case it's useful to anyone. I just want to be able to set an environment variable so that I can pushd
to the code.
#!/bin/bash
module=${1:?"I need a module name"}
python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI
Shell example:
[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
import a_module
print(a_module.__file__)
Will actually give you the path to the .pyc file that was loaded, at least on Mac OS X. So I guess you can do:
import os
path = os.path.abspath(a_module.__file__)
You can also try:
path = os.path.dirname(a_module.__file__)
To get the module's directory.
If you want to retrieve the package's root path from any of its modules, the following works (tested on Python 3.6):
from . import __path__ as ROOT_PATH
print(ROOT_PATH)
The main __init__.py
path can also be referenced by using __file__
instead.
Hope this helps!