4This module contains the main PYFT class.
6PYFT (Python FORTRAN Tool) is the primary class for reading, manipulating,
7and writing FORTRAN source files. It provides file-level operations and
8wraps the scope-level functionality from PYFTscope.
12>>> from pyfortool import PYFT
14# Read and modify a file
15>>> pft = PYFT('input.F90')
20>>> with PYFT('input.F90') as pft:
21... pft.removeComments()
27from multiprocessing
import Lock, RLock
31from pyfortool.util import (debugDecor, tostring, tofortran, fortran2xml,
32 setVerbosity, printInfos, PYFTError)
36def conservativePYFT(filename, parserOptions, wrapH,
37 tree=None, verbosity=None, clsPYFT=None):
39 Create a PYFT object with conservative parsing options.
41 Similar to PYFT constructor but forces the '-no-include' option
42 to prevent automatic inclusion of header files.
47 Path to the FORTRAN source file.
48 parserOptions : list or None
49 Parser options passed to PYFT. If None, uses default options.
51 Whether to wrap .h file content. See PYFT class.
53 Tree instance for cross-file analysis.
54 verbosity : str or int, optional
55 Logging verbosity level.
56 clsPYFT : type, optional
57 PYFT subclass to use instead of default PYFT.
62 A PYFT instance configured for conservative tree manipulation.
66 PYFT : The main PYFT class.
68 options = PYFT.DEFAULT_FXTRAN_OPTIONS
if parserOptions
is None else parserOptions
69 options = options.copy()
70 if len(set(options).intersection((
'-no-include',
'-noinclude'))) == 0:
72 options.append(
'-no-include')
75 pft = clsPYFT(filename, parserOptions=options, wrapH=wrapH,
76 tree=tree, verbosity=verbosity)
82 Generate a new PYFT object from a file path, creating the file if needed.
87 Path for the new file.
88 fortran : str, optional
89 FORTRAN source code to write to the file.
90 If None, creates a stub subroutine "SUBROUTINE FOO\nEND".
92 Additional arguments passed to PYFT constructor.
97 PYFT instance for the newly created file.
101 >>> pft = generateEmptyPYFT('new.F90', 'MODULE TEST\nEND MODULE')
102 >>> pft.addVar([('module:TEST', 'X', 'INTEGER :: X', None)])
105 with open(filename,
'w', encoding=
'utf-8')
as fo:
106 fo.write(
'SUBROUTINE FOO\nEND' if fortran
is None else fortran)
107 pft =
PYFT(filename, **kwargs)
109 pft.remove(pft.find(
'.//{*}program-unit'))
115 Main class for FORTRAN file manipulation.
117 PYFT extends PYFTscope with file-level operations (read/write) and
118 integrates with the fxtran parser for FORTRAN source code analysis.
122 DEFAULT_FXTRAN_OPTIONS : list
123 Default options for fxtran parser: ['-construct-tag', '-no-include',
124 '-no-cpp', '-line-length', '9999']
125 MANDATORY_FXTRAN_OPTIONS : list
126 Required options: ['-construct-tag']
131 >>> pft = PYFT('myfile.F90')
135 Using context manager:
136 >>> with PYFT('myfile.F90', output='output.F90') as pft:
137 ... pft.removeComments()
141 >>> tree = Tree(['/path/to/src'])
142 >>> PYFT.setParallel(tree)
144 DEFAULT_FXTRAN_OPTIONS = [
'-construct-tag',
'-no-include',
'-no-cpp',
'-line-length',
'9999']
145 MANDATORY_FXTRAN_OPTIONS = [
'-construct-tag']
147 NO_PARALLEL_LOCK =
None
148 PARALLEL_FILE_LOCKS =
None
150 @updateTree('signal')
151 def __init__(self, filename, output=None, parserOptions=None, verbosity=None,
152 wrapH=False, tree=None, enableCache=False):
154 Initialize a PYFT instance from a FORTRAN source file.
159 Path to the input FORTRAN file.
160 output : str, optional
161 Path for output file. If None, overwrites the input file.
162 parserOptions : list, optional
163 Options for the fxtran parser. See DEFAULT_FXTRAN_OPTIONS.
164 verbosity : str or int, optional
165 Logging level (e.g., 'DEBUG', 'INFO', 'WARNING').
166 wrapH : bool, optional
167 If True, wrap .h file content in a MODULE to enable parsing
168 as free-form FORTRAN.
169 tree : Tree, optional
170 Tree instance for cross-file analysis. If None, creates a new Tree.
171 enableCache : bool, optional
172 If True, cache parent nodes for faster traversal.
177 If Python version < 3.8 or file does not exist.
181 >>> pft = PYFT('myfile.F90')
182 >>> pft = PYFT('myfile.F90', output='newfile.F90')
183 >>> pft = PYFT('code.h', wrapH=True)
186 if not sys.version_info >= (3, 8):
189 raise PYFTError(
"PyForTool needs at least version 3.8 of python")
192 assert os.path.exists(filename), f
'Input filename ({filename})must exist'
194 tree =
Tree()
if tree
is None else tree
196 assert tree
is not None,
'tree must be None if setParallel has been called'
198 if parserOptions
is None:
202 for tDir
in tree.getDirs():
208 super().
__init__(xml, enableCache=enableCache, tree=tree)
210 self.
tree.signal(self)
211 if verbosity
is not None:
212 setVerbosity(verbosity)
217 Configure PYFT for parallel processing.
219 Must be called before creating PYFT instances for parallel execution.
220 Sets up shared tree and file locking mechanisms.
225 Tree object shared among all processes.
226 clsLock : type, optional
227 Lock class to use. Defaults to multiprocessing.Lock.
228 clsRLock : type, optional
229 Recursive lock class. Defaults to multiprocessing.RLock.
233 >>> tree = Tree(['/path/to/src'], descTreeFile='tree.json')
234 >>> PYFT.setParallel(tree)
235 >>> # Now create PYFT instances in parallel processes
244 for file
in tree.getFiles()}
249 Acquire file lock for parallel processing.
254 Path to the file to lock.
256 filename = os.path.normpath(filename)
265 Release file lock for parallel processing.
270 Path to the file to unlock.
271 silence : bool, optional
272 If True, suppress ValueError when file is not locked.
274 filename = os.path.normpath(filename)
286 Enter context manager.
291 Self reference for use in with statement.
297 Exit context manager and close file.
303 Close the FORTRAN file and release resources.
305 Prints debug statistics and releases file locks.
306 Automatically called when exiting context manager.
314 Get the XML representation of the parsed code.
319 XML string representation of the FORTRAN source.
321 return tostring(self)
326 Get the FORTRAN source code representation.
331 FORTRAN source code string.
333 return tofortran(self)
337 Set output file extension to uppercase.
341 >>> pft = PYFT('file.F90')
342 >>> pft.renameUpper() # Output will be file.F90
348 Set output file extension to lowercase.
352 >>> pft = PYFT('file.F90')
353 >>> pft.renameLower() # Output will be file.f90
359 Apply a transformation function to the file extension.
364 Function to apply to file extension (e.g., str.upper, str.lower).
366 def _transExt(path, mod):
367 filename, ext = os.path.splitext(path)
368 return filename + mod(ext)
376 Write the transformed FORTRAN source to file.
378 Writes the current state of the code tree as FORTRAN source
379 to the output file (or overwrites input if no output specified).
382 encoding=
'utf-8')
as fo:
385 os.fsync(fo.fileno())
394 Write the internal XML representation to a file.
399 Path for the output XML file.
403 >>> pft = PYFT('input.F90')
404 >>> pft.writeXML('output.xml')
406 with open(filename,
'w', encoding=
'utf-8')
as fo: