ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Tools
PyUtils
bin
apydep.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3
#
4
# Created: Oct 2020, Frank Winklmeier
5
#
6
"""
7
Extract Python dependencies between packages and create DOT graph.
8
Both `import` and `include` dependencies are considered.
9
"""
10
11
import
ast
12
import
sys
13
import
os
14
import
argparse
15
import
pygraphviz
16
from
collections
import
defaultdict
17
18
class
DependencyFinder
(ast.NodeVisitor):
19
"""Walk an AST collecting import/include statements."""
20
21
def
__init__
(self):
22
self.
imports
=
set
()
23
self.
includes
=
set
()
24
25
def
visit_Import
(self, node):
26
"""import XYZ"""
27
self.
imports
.update(alias.name.split(
'.'
,1)[0]
for
alias
in
node.names)
28
29
def
visit_ImportFrom
(self, node):
30
"""from XYZ import ABC"""
31
if
node.level==0:
# ignore relative imports
32
self.
imports
.
add
(node.module.split(
'.'
,1)[0])
33
34
def
visit_Call
(self, node):
35
""""include(XYZ/ABC.py)"""
36
if
isinstance(node.func, ast.Name)
and
node.func.id==
'include'
and
node.args:
37
if
isinstance(node.args[0], ast.Constant):
38
self.
includes
.
add
(node.args[0].value.split(
'/'
,1)[0])
39
40
41
def
get_dependencies
(filename, print_error=False):
42
"""Get all the imports/includes in a file."""
43
44
try
:
45
tree = ast.parse(open(filename,
'rb'
).
read
(), filename=filename)
46
except
Exception
as
e:
47
if
print_error:
48
print
(e, file=sys.stderr)
49
return
DependencyFinder
()
50
51
finder =
DependencyFinder
()
52
try
:
53
finder.visit(tree)
54
except
Exception
as
e:
55
if
print_error:
56
print
(e, f
'({os.path.basename(filename)})'
, file=sys.stderr)
57
58
return
finder
59
60
61
def
walk_tree
(path='./', print_error=False, filterFnc=None):
62
"""Walk the source tree and extract python dependencies, filtered by FilterFnc"""
63
64
pkg =
'UNKNOWN'
65
deps = defaultdict(
lambda
: defaultdict(set))
66
for
root, dirs, files
in
os.walk(path):
67
if
'CMakeLists.txt'
in
files:
68
pkg = os.path.basename(root)
69
70
if
(filterFnc
and
not
filterFnc(pkg)):
71
continue
72
73
for
f
in
filter(
lambda
p : os.path.splitext(p)[1]==
'.py'
, files):
74
d =
get_dependencies
(os.path.join(root,f), print_error)
75
deps[pkg][
'import'
].update(d.imports)
76
deps[pkg][
'include'
].update(d.includes)
77
78
return
deps
79
80
81
def
make_graph
(deps, filterFnc=None):
82
"""Save the dependencies as dot graph, nodes filtered by filterFnc"""
83
84
graph = pygraphviz.AGraph(name=
'AthPyGraph'
, directed=
True
)
85
for
a
in
deps:
86
for
t
in
[
'import'
,
'include'
]:
87
graph.add_edges_from(((a,b)
for
b
in
deps[a][t]
88
if
a!=b
and
(filterFnc
is
None
or
(filterFnc(a)
and
filterFnc(b)))),
89
label = t)
90
return
graph
91
92
93
def
main
():
94
parser = argparse.ArgumentParser(description=__doc__)
95
96
parser.add_argument(
'path'
, metavar=
'DIRECTORY'
, nargs=
'?'
, default=
'./'
,
97
help=
'root of source tree [%(default)s]'
)
98
99
parser.add_argument(
'-o'
,
'--output'
, metavar=
'FILE'
, type=str,
100
help=
'output file for DOT graph'
)
101
102
parser.add_argument(
'-p'
,
'--packages'
, metavar=
'FILE'
, type=str,
103
help=
'path to packages.txt file [from release]'
)
104
105
parser.add_argument(
'-a'
,
'--all'
, action=
'store_true'
,
106
help=
'include non-athena dependencies'
)
107
108
parser.add_argument(
'-v'
,
'--verbose'
, action=
'store_true'
,
109
help=
'print parse errors'
)
110
111
args = parser.parse_args()
112
113
packages =
None
114
if
not
args.all:
115
package_file = args.packages
or
os.path.join(os.environ[
'AtlasArea'
],
'InstallArea'
,
116
os.environ[
'BINARY_TAG'
],
'packages.txt'
)
117
118
try
:
119
with
open(package_file)
as
f:
120
packages =
set
(line.rstrip().
split
(
'/'
)[-1]
for
line
in
f
if
not
line.startswith(
'#'
))
121
except
FileNotFoundError:
122
parser.error(f
"Cannot read '{package_file}'. Specify via '-p/--packages' or run with '-a/--all'"
)
123
124
# By default only show athena packages:
125
filterFnc =
None
if
args.all
else
lambda
p : p
in
packages
126
127
# Walk source tree and create DOT graph:
128
g =
make_graph
(
walk_tree
(args.path, args.verbose, filterFnc), filterFnc)
129
130
if
args.output:
131
g.write(args.output)
132
else
:
133
print
(g)
134
135
if
__name__ ==
"__main__"
:
136
sys.exit(
main
())
print
void print(char *figname, TCanvas *c1)
Definition
TRTCalib_StrawStatusPlots.cxx:26
apydep.DependencyFinder
Definition
apydep.py:18
apydep.DependencyFinder.__init__
__init__(self)
Definition
apydep.py:21
apydep.DependencyFinder.visit_Call
visit_Call(self, node)
Definition
apydep.py:34
apydep.DependencyFinder.imports
imports
Definition
apydep.py:22
apydep.DependencyFinder.visit_Import
visit_Import(self, node)
Definition
apydep.py:25
apydep.DependencyFinder.visit_ImportFrom
visit_ImportFrom(self, node)
Definition
apydep.py:29
apydep.DependencyFinder.includes
includes
Definition
apydep.py:23
set
STL class.
add
bool add(const std::string &hname, TKey *tobj)
Definition
fastadd.cxx:55
split
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition
hcg.cxx:179
apydep.get_dependencies
get_dependencies(filename, print_error=False)
Definition
apydep.py:41
apydep.make_graph
make_graph(deps, filterFnc=None)
Definition
apydep.py:81
apydep.main
main()
Definition
apydep.py:93
apydep.walk_tree
walk_tree(path='./', print_error=False, filterFnc=None)
Definition
apydep.py:61
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition
openCoraCool.cxx:569
Generated on
for ATLAS Offline Software by
1.17.0