ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Trigger
TrigValidation
TrigValTools
bin
chainComp.py
Go to the documentation of this file.
1
#!/usr/bin/env python
2
#
3
# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
4
#
5
6
'''
7
Script to compare trigger chain counts in YAML format produced by
8
chainDump.py --yaml and providing information and instructions for
9
updating counts reference in the athena repository.
10
'''
11
12
import
sys
13
import
os
14
import
argparse
15
import
logging
16
import
yaml
17
import
glob
18
import
subprocess
19
from
AthenaCommon.Utils
import
unixtools
20
from
TrigValTools.TrigValSteering.Common
import
package_prefix_dict
as
packages
21
from
TrigValTools.TrigValSteering.Common
import
running_in_CI
22
23
24
def
get_parser
():
25
parser = argparse.ArgumentParser(usage=
'%(prog)s [options] inputFile'
,
26
description=__doc__)
27
parser.add_argument(
'inputFile'
,
28
metavar=
'PATH'
,
29
help=
'Name of input counts file'
)
30
parser.add_argument(
'-r'
,
'--referenceFile'
,
31
metavar=
'PATH'
,
32
help=
'Name of reference counts file. If empty, try to guess from inputFile'
)
33
parser.add_argument(
'-s'
,
'--sourceRepository'
,
34
metavar=
'PATH'
,
35
help=
'Path to the git checkout of the athena repository.'
36
'If empty, try to guess from the environment'
)
37
parser.add_argument(
'-p'
,
'--patch'
,
38
action=
'store_true'
,
39
help=
'Create a git patch file with the count differences'
)
40
parser.add_argument(
'-m'
,
'--matchingOnly'
,
41
action=
'store_true'
,
42
help=
'Ignore added and removed chains, and only compare counts for chains '
+ \
43
'present in both reference and input file'
)
44
parser.add_argument(
'-v'
,
'--verbose'
,
45
action=
'store_true'
,
46
help=
'Increase output verbosity'
)
47
return
parser
48
49
50
def
find_reference
(inputFile):
51
ref_path =
None
52
if
inputFile.endswith(
'.new'
):
53
inp_name = os.path.split(inputFile)[-1]
54
ref_name = inp_name[0:-4] +
'.ref'
55
logging.debug(
'Searching for file %s in DATAPATH'
, ref_name)
56
for
pkg_name
in
packages:
57
ref_path = unixtools.find_datafile(pkg_name+
'/'
+ref_name)
58
if
ref_path:
59
break
60
if
ref_path:
61
logging.debug(
'Found %s'
, ref_path)
62
else
:
63
logging.debug(
'Not found'
)
64
return
ref_path
65
66
67
def
find_repository
(ref_path):
68
'''
69
Try to find the athena git repository based on paths in env.
70
Needs to work both in local WorkDir build and in full/incremental CI build.
71
'''
72
logging.debug(
'Searching for the source repository path'
)
73
search_str =
'/Trigger/TrigValidation'
74
def
try_path(p):
75
logging.debug(
'Trying %s'
, p)
76
if
os.path.islink(p)
and
os.path.isfile(p)
and
search_str
in
os.path.realpath(p):
77
candidate = os.path.realpath(p).
split
(search_str)[0]
78
logging.debug(
'Candidate %s'
, candidate)
79
if
os.path.isdir(candidate +
'/.git'
):
80
return
candidate
81
return
None
82
83
result = try_path(ref_path)
84
if
result:
85
return
result
86
87
env_vars = [
'DATAPATH'
,
'JOBOPTSEARCHPATH'
,
'CALIBPATH'
,
'XMLPATH'
]
88
path_lists = [os.getenv(var).
split
(os.pathsep)
for
var
in
env_vars]
89
max_len =
max
([len(pl)
for
pl
in
path_lists])
90
max_len =
min
(max_len, 2)
# Limit to first two paths to avoid very deep and slow search
91
for
i
in
range(max_len):
92
for
pl
in
path_lists:
93
if
i >= len(pl):
94
continue
95
paths = glob.glob(pl[i]+
'/**'
, recursive=
True
)
96
for
p
in
paths:
97
result = try_path(p)
98
if
result:
99
return
result
100
return
None
101
102
103
def
load_yaml
(path):
104
result =
None
105
if
not
os.path.isfile(path):
106
logging.error(
'Not a file: %s'
, path)
107
return
result
108
with
open(path)
as
f:
109
result = yaml.safe_load(f)
110
return
result
111
112
113
def
print_event_diff
(inp_data, ref_data, key):
114
if
inp_data[key] != ref_data[key]:
115
logging.info(
' %s: %d -> %d'
, key, ref_data[key], inp_data[key])
116
117
118
def
print_step_diff
(inp_data, ref_data, key):
119
inp_steps = inp_data.get(key, dict())
120
ref_steps = ref_data.get(key, dict())
121
if
inp_steps != ref_steps:
122
logging.info(
' %s:'
, key)
123
for
step
in
range(
max
(len(inp_steps), len(ref_steps))):
124
inp_count = inp_steps.get(step, 0)
125
ref_count = ref_steps.get(step, 0)
126
if
inp_count != ref_count:
127
logging.info(
' %d: %d -> %d'
, step, ref_count, inp_count)
128
129
130
def
print_diff
(inp_dict, ref_dict, matching_only=False):
131
retcode = 0
132
chains_not_in_ref = [c[0]
for
c
in
inp_dict.items()
if
c[0]
not
in
ref_dict]
133
chains_not_in_inp = [c[0]
for
c
in
ref_dict.items()
if
c[0]
not
in
inp_dict]
134
chains_count_diff = [c[0]
for
c
in
inp_dict.items()
if
c[0]
in
ref_dict
and
c[1] != ref_dict[c[0]]]
135
136
# New chains (missing in reference)
137
n_new_chains = len(chains_not_in_ref)
138
if
n_new_chains > 0
and
not
matching_only:
139
retcode = 1
140
logging.info(
'Found %d new chain%s added:'
, n_new_chains,
's'
if
n_new_chains > 1
else
''
)
141
for
chain_name
in
chains_not_in_ref:
142
logging.info(
' %s'
, chain_name)
143
144
# Removed chains (missing in test file)
145
n_removed_chains = len(chains_not_in_inp)
146
if
n_removed_chains > 0
and
not
matching_only:
147
retcode = 1
148
logging.info(
'Found %d chain%s removed:'
, n_removed_chains,
's'
if
n_removed_chains > 1
else
''
)
149
for
chain_name
in
chains_not_in_inp:
150
logging.info(
' %s'
, chain_name)
151
152
# Count differences
153
n_diff_chains = len(chains_count_diff)
154
if
n_diff_chains > 0:
155
retcode = 1
156
logging.info(
'Found %d chain%s with count differences:'
, n_diff_chains,
's'
if
n_diff_chains > 1
else
''
)
157
for
chain_name
in
chains_count_diff:
158
logging.info(
' %s:'
, chain_name)
159
inp_data = inp_dict[chain_name]
160
ref_data = ref_dict[chain_name]
161
print_event_diff
(inp_data, ref_data,
'eventCount'
)
162
print_step_diff
(inp_data, ref_data,
'stepCounts'
)
163
print_step_diff
(inp_data, ref_data,
'stepFeatures'
)
164
165
return
retcode
166
167
168
def
create_patch
(inp_path, ref_path):
169
def
find_ref_package():
170
for
path
in
[ref_path,
find_reference
(inp_path),
find_reference
(ref_path)]:
171
for
pkg
in
packages:
172
if
'/data/'
+pkg
in
ref_path:
173
return
pkg, os.path.split(path)[1]
174
return
None
,
None
175
package_name, ref_file_name = find_ref_package()
176
if
not
package_name:
177
logging.warning(
'Failed to find reference package name, cannot create git patch file'
)
178
return
179
180
ref_rel_path =
'/Trigger/TrigValidation/'
+ package_name +
'/share/'
+ ref_file_name
181
182
diff_cmd =
'diff -U 1 -b'
183
diff_cmd +=
' --label a'
+ ref_rel_path +
' '
+ ref_path
184
diff_cmd +=
' --label b'
+ ref_rel_path +
' '
+ inp_path
185
186
diff_txt = subprocess.getoutput(diff_cmd)
187
patch_name = os.path.split(ref_path)[1]
188
if
patch_name.endswith(
'.ref'
)
or
patch_name.endswith(
'.new'
):
189
patch_name = patch_name[0:-4]
190
patch_name +=
'.patch'
191
with
open(patch_name,
'w'
)
as
patch_file:
192
patch_file.write(
'diff --git a{p:s} b{p:s}\n'
.format(p=ref_rel_path))
193
patch_file.write(diff_txt +
'\n'
)
194
logging.info(
'Patch file created. To apply, run in the athena source directory:'
)
195
logging.info(
'git apply %s/%s'
, os.getcwd(), patch_name)
196
logging.info(
'Then check with git diff and, if everything is correct, add and commit'
)
197
if
running_in_CI():
198
logging.info(
'or see the patch below:\n'
)
199
subprocess.call(
'cat '
+ patch_name, shell=
True
)
200
print
(
'\n'
)
# noqa: ATL901
201
202
203
def
main
():
204
args =
get_parser
().parse_args()
205
logging.basicConfig(stream=sys.stdout,
206
format=
'chainComp %(levelname)-8s %(message)s'
,
207
level=logging.DEBUG
if
args.verbose
else
logging.INFO)
208
209
inp_path = args.inputFile
210
ref_path = args.referenceFile
or
find_reference
(inp_path)
211
if
not
ref_path:
212
logging.error(
'No reference file specified and couldn\'t guess from '
213
'input name. Please use the -r argument (see --help).'
)
214
return
1
215
216
logging.info(
'Test file: %s'
, inp_path)
217
logging.info(
'Reference file: %s'
, ref_path)
218
219
# Currently not used, but ready for implementing checks against upstream
220
# src_path = args.sourceRepository or find_repository(ref_path)
221
# logging.info('Repository path: %s', src_path)
222
223
inp_dict =
load_yaml
(inp_path)
224
ref_dict =
load_yaml
(ref_path)
225
if
(
not
inp_dict)
or
(
not
ref_dict):
226
return
1
227
228
retcode =
print_diff
(inp_dict, ref_dict, args.matchingOnly)
229
if
retcode:
230
logging.error(
'Trigger counts differ from the reference. If the above differences are intended, update the reference'
)
231
if
args.patch:
232
create_patch
(inp_path, ref_path)
233
else
:
234
logging.info(
'Trigger counts match the reference'
)
235
236
return
retcode
237
238
239
if
'__main__'
in
__name__:
240
sys.exit(
main
())
print
void print(char *figname, TCanvas *c1)
Definition
TRTCalib_StrawStatusPlots.cxx:26
min
#define min(a, b)
Definition
cfImp.cxx:40
max
#define max(a, b)
Definition
cfImp.cxx:41
split
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition
hcg.cxx:179
chainComp.print_event_diff
print_event_diff(inp_data, ref_data, key)
Definition
chainComp.py:113
chainComp.main
main()
Definition
chainComp.py:203
chainComp.find_reference
find_reference(inputFile)
Definition
chainComp.py:50
chainComp.load_yaml
load_yaml(path)
Definition
chainComp.py:103
chainComp.print_diff
print_diff(inp_dict, ref_dict, matching_only=False)
Definition
chainComp.py:130
chainComp.get_parser
get_parser()
Definition
chainComp.py:24
chainComp.print_step_diff
print_step_diff(inp_data, ref_data, key)
Definition
chainComp.py:118
chainComp.create_patch
create_patch(inp_path, ref_path)
Definition
chainComp.py:168
chainComp.find_repository
find_repository(ref_path)
Definition
chainComp.py:67
Generated on
for ATLAS Offline Software by
1.17.0