For managing transition from an old import to a new import, I search to log from where my module is used.
By example, my old is name m_old.py. With a simple print in file m_old.py, I display a message "used of old module". So each time there is "import m_old.py", I have a message "used of old module" in execution ?
If it was a function, I will use module inspect and traceback to log in execution from where this function is call, by exemple "call of old function from file.py line XXX".
Is-it possible to display a message like : "this old import is call from file.py line XXX" ?
For managing transition from an old import to a new import, I search to log from where my module is used.
By example, my old is name m_old.py. With a simple print in file m_old.py, I display a message "used of old module". So each time there is "import m_old.py", I have a message "used of old module" in execution ?
If it was a function, I will use module inspect and traceback to log in execution from where this function is call, by exemple "call of old function from file.py line XXX".
Is-it possible to display a message like : "this old import is call from file.py line XXX" ?
Share Improve this question edited Mar 27 at 7:31 Emmanuel DUMAS asked Mar 26 at 12:51 Emmanuel DUMASEmmanuel DUMAS 73911 silver badges28 bronze badges 3 |2 Answers
Reset to default 1I have a similar problem: I want to know which module import which library/module, so I wrote a short script:
#!/usr/bin/env python3
"""show_imports.py: Show imports from files"""
import argparse
import ast
from collections import defaultdict
from pathlib import Path
def find_imports(path: Path, found: dict):
content = path.read_text()
module = ast.parse(content, path)
for entity in ast.walk(module):
if isinstance(entity, ast.Import):
for alias in entity.names:
found[alias.name].add((path, entity.lineno))
elif isinstance(entity, ast.ImportFrom):
found[entity.module].add((path, entity.lineno))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("dir")
options = parser.parse_args()
root = Path(options.dir).resolve().relative_to(Path.cwd())
if root.is_file():
paths = [root]
elif root.is_dir():
paths = root.rglob("*.py")
else:
raise SystemExit(f"{root} is not a valid dir or file")
found = defaultdict(set)
for path in paths:
find_imports(path, found)
for mod, paths in found.items():
print(mod)
for path, lineno in sorted(paths):
print(f" {path}({lineno})")
if __name__ == "__main__":
main()
You can run this script and pass in either a single python script, or a directory. In the case of directory, all python scripts in there will be analyzed. Here is a sample run of the script against itself:
$ ./show_imports.py show_imports.py
argparse
show_imports.py(4)
ast
show_imports.py(5)
collections
show_imports.py(6)
pathlib
show_imports.py(7)
Notes
- I use the
ast
library to parse each Python script - The
ast.walk()
will walk through all entities in the script, but I only care about theast.Import
andast.ImportFrom
entities
After investigation on module traceback, I found my solution :
traceback.extract_stack()
And analyse list in many level (around 10) and I have found file and line of import.
Can also use inspect, by example like this :
import inspect
fl = inspect.stack()
cnt = 0
for f in fl:
# print("f=", f.filename, " l=", f.lineno)
if f.filename[0:7] != "<frozen":
# print(" >>>>> f=", f.filename, " l=", f.lineno)
cnt += 1
if cnt == 2:
print("WARNING : %s(%d) replace import m_old by import m_new" % (f.filename, f.lineno))
module.__file__
to get its path – cards Commented Mar 26 at 13:00import m_old
? – John Gordon Commented Mar 26 at 13:24m_old
was already created and not execute your code again. – chepner Commented Mar 26 at 13:44