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 Remove parallel processing configuration
258 Acquire file lock for parallel processing.
263 Path to the file to lock.
265 filename = os.path.normpath(filename)
274 Release file lock for parallel processing.
279 Path to the file to unlock.
280 silence : bool, optional
281 If True, suppress ValueError when file is not locked.
283 filename = os.path.normpath(filename)
295 Enter context manager.
300 Self reference for use in with statement.
306 Exit context manager and close file.
312 Close the FORTRAN file and release resources.
314 Prints debug statistics and releases file locks.
315 Automatically called when exiting context manager.
323 Get the XML representation of the parsed code.
328 XML string representation of the FORTRAN source.
330 return tostring(self)
335 Get the FORTRAN source code representation.
340 FORTRAN source code string.
342 return tofortran(self)
346 Set output file extension to uppercase.
350 >>> pft = PYFT('file.F90')
351 >>> pft.renameUpper() # Output will be file.F90
357 Set output file extension to lowercase.
361 >>> pft = PYFT('file.F90')
362 >>> pft.renameLower() # Output will be file.f90
368 Apply a transformation function to the file extension.
373 Function to apply to file extension (e.g., str.upper, str.lower).
375 def _transExt(path, mod):
376 filename, ext = os.path.splitext(path)
377 return filename + mod(ext)
385 Write the transformed FORTRAN source to file.
387 Writes the current state of the code tree as FORTRAN source
388 to the output file (or overwrites input if no output specified).
391 encoding=
'utf-8')
as fo:
394 os.fsync(fo.fileno())
403 Write the internal XML representation to a file.
408 Path for the output XML file.
412 >>> pft = PYFT('input.F90')
413 >>> pft.writeXML('output.xml')
415 with open(filename,
'w', encoding=
'utf-8')
as fo: