27 Decorator to trace function calls with timing and argument logging.
29 When logging is enabled at DEBUG level, logs function calls with arguments.
30 When logging is enabled at INFO level, tracks call count and execution time.
33 def wrapper(*args, **kwargs):
35 logger = logging.getLogger()
36 if logger.isEnabledFor(logging.DEBUG):
37 callstr = func.__name__ + \
38 '(' +
', '.join([str(a)
for a
in args] +
39 [k +
'=' + str(v)
for (k, v)
in kwargs.items()]) +
')'
40 logging.debug(
'%s --> ...', callstr)
45 if logger.isEnabledFor(logging.INFO):
51 result = func(*args, **kwargs)
56 if func.__name__
not in debugStats:
57 debugStats[func.__name__] = {
'nb': 0,
'totalTime': 0}
58 debugStats[func.__name__][
'nb'] += 1
59 duration = time.time() - t0
60 debugStats[func.__name__][
'totalTime'] += duration
61 debugStats[func.__name__][
'min'] = \
62 min(duration, debugStats[func.__name__].get(
'min', duration))
63 debugStats[func.__name__][
'max'] = \
64 max(duration, debugStats[func.__name__].get(
'max', duration))
67 if callstr
is not None:
69 logging.debug(
'%s --> %s', callstr, str(result))
77 Decorator to prevent parallel execution of a method.
79 Used for methods that modify the XML tree and need to prevent
80 concurrent execution across multiple files.
83 def wrapper(self, *args, **kwargs):
84 if self.NO_PARALLEL_LOCK
is not None:
86 with self.NO_PARALLEL_LOCK:
92 if self.SHARED_TREE
is not None:
93 self.tree.copyFromOtherTree(self.SHARED_TREE)
94 result = func(self, *args, **kwargs)
95 if self.SHARED_TREE
is not None:
96 self.tree.copyToOtherTree(self.SHARED_TREE)
99 return func(self, *args, **kwargs)
121 Print debug statistics on decorated function usage.
123 Displays a table with function names, call counts, min/max/total
124 execution times for functions decorated with @debugDecor.
126 logger = logging.getLogger()
127 if logger.isEnabledFor(logging.INFO):
128 def _print(name, nb, vmin, vmax, mean):
129 print(
'| ' + name.ljust(30) +
'| ' + str(nb).ljust(14) +
'| ' +
130 str(vmin).ljust(23) +
'| ' + str(vmax).ljust(23) +
'| ' +
131 str(mean).ljust(23) +
'|')
132 _print(
'Name of the function',
'# of calls',
'Min (s)',
'Max (s)',
'Total (s)')
133 for funcName, values
in debugStats.items():
134 _print(funcName, values[
'nb'], values[
'min'], values[
'max'], values[
'totalTime'])
146def fortran2xml(fortranSource, parserOptions=None, wrapH=False):
148 Convert FORTRAN source code to XML using fxtran parser.
153 FORTRAN source code string or path to a file.
154 parserOptions : list, optional
155 Options passed to fxtran parser.
156 wrapH : bool, optional
157 If True, wrap .h file content in a MODULE for free-form parsing.
162 (includesRemoved, xml) where:
163 - includesRemoved (bool): True if include statements were processed.
164 - xml (Element): XML document tree.
168 >>> includesRemoved, xml = fortran2xml("REAL :: X\nX = 1.0")
171 ET.register_namespace(
'f', NAMESPACE)
174 if parserOptions
is None:
176 parserOptions = pyfortool.PYFT.DEFAULT_FXTRAN_OPTIONS
181 with tempfile.NamedTemporaryFile(buffering=0, suffix=
'.F90')
as file:
182 if os.path.exists(fortranSource):
186 filename = fortranSource
187 if wrapH
and filename.endswith(
'.h'):
190 with open(fortranSource,
'r', encoding=
'utf-8')
as src:
195 firstLine = [line
for line
in content.split(
'\n')
196 if not (re.search(
r'^[\t ]*!', line)
or
197 re.search(
r'^[\t ]*$', line))][0]
198 fisrtLine = firstLine.upper().split()
199 if not (
'SUBROUTINE' in fisrtLine
or 'FUNCTION' in firstLine):
202 content =
'MODULE FOO\n' + content +
'\nEND MODULE FOO'
203 file.write(content.encode(
'UTF8'))
206 file.write(fortranSource.encode(
'UTF-8'))
207 xml = pyfxtran.run(filename, [
'-o',
'-'] + parserOptions)
208 xml = ET.fromstring(xml, parser=ET.XMLParser(encoding=
'UTF-8'))
210 xml.find(
'./{*}file').attrib[
'name'] = fortranSource
212 file = xml.find(
'./{*}file')
213 programUnit = file.find(
'./{*}program-unit')
215 for node
in programUnit[1:-1]:
218 node.tail = node.tail[:-1]
219 file.remove(programUnit)
222 if len(set([
'-no-include',
'-noinclude']).intersection(parserOptions)) == 0:
229 includeStmts = xml.findall(
'.//{*}include')
230 for includeStmt
in includeStmts:
231 par = [p
for p
in xml.iter()
if includeStmt
in p][0]
232 par.remove(includeStmt)
235 mainfile = xml.find(
'./{*}file')
236 for file
in mainfile.findall(
'.//{*}file'):
237 par = [p
for p
in xml.iter()
if file
in p][0]
238 index = list(par).index(file)
239 if file.tail
is not None:
240 file[-1].tail = file.tail
if file[-1].tail
is None else (file[-1].tail + file.tail)
241 for node
in file[::-1]:
242 par.insert(index, node)
245 return includesDone, xml