Reference Guide  2.5.0
psyclone.parse.module_info.ModuleInfo Class Reference

Public Member Functions

def __init__ (self, name, filename)
 
def name (self)
 
def filename (self)
 
def get_source_code (self)
 
def get_parse_tree (self)
 
def contains_routine (self, routine_name)
 
def get_used_modules (self)
 
def get_used_symbols_from_modules (self)
 
def get_psyir (self)
 
def resolve_routine (self, routine_name)
 

Detailed Description

This class stores mostly cached information about modules: it stores
the original filename, if requested it will read the file and then caches
the plain text file, and if required it will parse the file, and then
cache the fparser AST.

:param str name: the module name.
:param str filename: the name of the source file that stores this module \
    (including path).

Definition at line 73 of file module_info.py.

Member Function Documentation

◆ contains_routine()

def psyclone.parse.module_info.ModuleInfo.contains_routine (   self,
  routine_name 
)
:returns: whether the specified routine name is part of this
    module or not. It will also return False if the file could
    not be parsed.
:rtype: bool

Definition at line 218 of file module_info.py.

218  def contains_routine(self, routine_name):
219  ''':returns: whether the specified routine name is part of this
220  module or not. It will also return False if the file could
221  not be parsed.
222  :rtype: bool
223 
224  '''
225  # TODO #2422 and TODO #2478: Once we parse everything to PSyIR (esp.
226  # generic interfaces), this routine can just be replaced with
227  # get_psyir().get_routine_psyir(routine_name)
228  if self._routine_names is None:
229  # This will trigger adding routine information
230  try:
231  self.get_parse_tree()
232  except FortranSyntaxError:
233  return False
234 
235  return routine_name.lower() in self._routine_names
236 

References psyclone.parse.module_info.ModuleInfo._routine_names, psyclone.parse.module_info.ModuleInfo._used_modules, psyclone.parse.module_info.ModuleInfo._used_symbols_from_module, and psyclone.parse.module_info.ModuleInfo.get_parse_tree().

Here is the call graph for this function:

◆ filename()

def psyclone.parse.module_info.ModuleInfo.filename (   self)
:returns: the filename that contains the source code for this \
    module.
:rtype: str

Definition at line 133 of file module_info.py.

133  def filename(self):
134  ''':returns: the filename that contains the source code for this \
135  module.
136  :rtype: str
137 
138  '''
139  return self._filename
140 

References psyclone.parse.module_info.ModuleInfo._filename.

◆ get_parse_tree()

def psyclone.parse.module_info.ModuleInfo.get_parse_tree (   self)
Returns the fparser AST for this module. The first time, the file
will be parsed by fparser using the Fortran 2008 standard. The AST is
then cached for any future uses.

:returns: the fparser AST for this module.
:rtype: :py:class:`fparser.two.Fortran2003.Program`

Definition at line 164 of file module_info.py.

164  def get_parse_tree(self):
165  '''Returns the fparser AST for this module. The first time, the file
166  will be parsed by fparser using the Fortran 2008 standard. The AST is
167  then cached for any future uses.
168 
169  :returns: the fparser AST for this module.
170  :rtype: :py:class:`fparser.two.Fortran2003.Program`
171 
172  '''
173  if self._parse_tree is None:
174  # Set routine_names to be an empty set (it was None before).
175  # This way we avoid that any other function might trigger to
176  # parse this file again (in case of parsing errors).
177  self._routine_names = set()
178 
179  reader = FortranStringReader(self.get_source_code())
180  parser = ParserFactory().create(std="f2008")
181  self._parse_tree = parser(reader)
182 
183  # First collect information about all subroutines/functions.
184  # Store information about generic interface to be handled later
185  # (so we only walk the tree once):
186  # TODO #2478: once generic interfaces are supported, use PSyIR
187  # instead of fparser here.
188  all_generic_interfaces = []
189  for routine in walk(self._parse_tree, (Function_Subprogram,
190  Subroutine_Subprogram,
191  Interface_Block)):
192  if isinstance(routine, Interface_Block):
193  all_generic_interfaces.append(routine)
194  else:
195  routine_name = str(routine.content[0].items[1])
196  self._routine_names.add(routine_name)
197 
198  # Then handle all generic interfaces and add them to
199  # _generic_interfaces:
200  for interface in all_generic_interfaces:
201  # TODO #2422 This code does not support all potential
202  # interface statements. After #2422 we can use PSyIR here.
203  # Get the name of the interface from the Interface_Stmt:
204  name = str(walk(interface, Interface_Stmt)[0].items[0]).lower()
205  self._routine_names.add(name)
206 
207  # Collect all specific functions for this generic interface
208  routine_names = []
209  for proc_stmt in walk(interface, Procedure_Stmt):
210  # Convert the items to strings:
211  routine_names.extend([str(i) for i in
212  proc_stmt.items[0].items])
213  self._generic_interfaces[name] = routine_names
214 
215  return self._parse_tree
216 

References psyclone.parse.module_info.ModuleInfo._generic_interfaces, psyclone.parse.module_info.ModuleInfo._parse_tree, psyclone.parse.module_info.ModuleInfo._routine_names, and psyclone.parse.module_info.ModuleInfo.get_source_code().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_psyir()

def psyclone.parse.module_info.ModuleInfo.get_psyir (   self)
Returns the PSyIR representation of this module. This is based
on the fparser tree (see get_parse_tree), and the information is
cached. If the PSyIR must be modified, it needs to be copied,
otherwise the modified tree will be returned from the cache in the
future.
If the conversion to PSyIR fails, a dummy FileContainer with an
empty Container (module) is returned, which avoids additional error
handling in many other subroutines.
#TODO 2120: This should be revisited when improving on the error
handling.

:param routine_name: optional the name of a routine.
:type routine_name: Optional[str]

:returns: PSyIR representing this module.
:rtype: list[:py:class:`psyclone.psyir.nodes.Node`]

Definition at line 312 of file module_info.py.

312  def get_psyir(self):
313  '''Returns the PSyIR representation of this module. This is based
314  on the fparser tree (see get_parse_tree), and the information is
315  cached. If the PSyIR must be modified, it needs to be copied,
316  otherwise the modified tree will be returned from the cache in the
317  future.
318  If the conversion to PSyIR fails, a dummy FileContainer with an
319  empty Container (module) is returned, which avoids additional error
320  handling in many other subroutines.
321  #TODO 2120: This should be revisited when improving on the error
322  handling.
323 
324  :param routine_name: optional the name of a routine.
325  :type routine_name: Optional[str]
326 
327  :returns: PSyIR representing this module.
328  :rtype: list[:py:class:`psyclone.psyir.nodes.Node`]
329 
330  '''
331  if self._psyir is None:
332  try:
333  self._psyir = \
334  self._processor.generate_psyir(self.get_parse_tree())
335  except (KeyError, SymbolError, InternalError,
336  FortranSyntaxError) as err:
337  print(f"Error trying to parse '{self.filename}': '{err}'")
338  # TODO #11: Add proper logging
339  # TODO #2120: Handle error better. Long term we should not
340  # just ignore errors.
341  # Create a dummy FileContainer with a dummy module. This avoids
342  # additional error handling in other subroutines, since they
343  # will all return 'no information', whatever you ask for
344  self._psyir = FileContainer(os.path.basename(self._filename))
345  module = Container("invalid-module")
346  self._psyir.children.append(module)
347 
348  # TODO #2462: needs to be fixed to properly support multiple modules
349  # in one file
350  # Return the actual module Container (not the FileContainer)
351  return self._psyir.children[0]
352 

References psyclone.parse.module_info.ModuleInfo._filename, psyclone.parse.module_info.ModuleInfo._processor, psyclone.psyir.frontend.fortran.FortranReader._processor, psyclone.parse.module_info.ModuleInfo._psyir, and psyclone.parse.module_info.ModuleInfo.get_parse_tree().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_source_code()

def psyclone.parse.module_info.ModuleInfo.get_source_code (   self)
Returns the source code for the module. The first time, it
will be read from the file, but the data is then cached.

:returns: the source code.
:rtype: str

:raises ModuleInfoError: when the file cannot be read.

Definition at line 142 of file module_info.py.

142  def get_source_code(self):
143  '''Returns the source code for the module. The first time, it
144  will be read from the file, but the data is then cached.
145 
146  :returns: the source code.
147  :rtype: str
148 
149  :raises ModuleInfoError: when the file cannot be read.
150 
151  '''
152  if self._source_code is None:
153  try:
154  with open(self._filename, "r", encoding='utf-8') as file_in:
155  self._source_code = file_in.read()
156  except FileNotFoundError as err:
157  raise ModuleInfoError(
158  f"Could not find file '{self._filename}' when trying to "
159  f"read source code for module '{self._name}'") from err
160 
161  return self._source_code
162 

References psyclone.parse.module_info.ModuleInfo._filename, and psyclone.parse.module_info.ModuleInfo._source_code.

Here is the caller graph for this function:

◆ get_used_modules()

def psyclone.parse.module_info.ModuleInfo.get_used_modules (   self)
This function returns a set of all modules `used` in this
module. Fortran `intrinsic` modules will be ignored. The information
is based on the fparser parse tree of the module (since fparser can
handle more files than PSyir, like LFRic's `constants_mod` which has
pre-processor directives).

:returns: a set with all imported module names.
:rtype: set[str]

Definition at line 278 of file module_info.py.

278  def get_used_modules(self):
279  '''This function returns a set of all modules `used` in this
280  module. Fortran `intrinsic` modules will be ignored. The information
281  is based on the fparser parse tree of the module (since fparser can
282  handle more files than PSyir, like LFRic's `constants_mod` which has
283  pre-processor directives).
284 
285  :returns: a set with all imported module names.
286  :rtype: set[str]
287 
288  '''
289  if self._used_modules is None:
290  self._extract_import_information()
291 
292  return self._used_modules
293 

References psyclone.parse.module_info.ModuleInfo._extract_import_information(), and psyclone.parse.module_info.ModuleInfo._used_modules.

Here is the call graph for this function:

◆ get_used_symbols_from_modules()

def psyclone.parse.module_info.ModuleInfo.get_used_symbols_from_modules (   self)
This function returns information about which modules are used by
this module, and also which symbols are imported. The return value is
a dictionary with the used module name as key, and a set of all
imported symbol names as value.

:returns: a dictionary that gives for each module name the set \
    of symbols imported from it.
:rtype: dict[str, set[str]]

Definition at line 295 of file module_info.py.

295  def get_used_symbols_from_modules(self):
296  '''This function returns information about which modules are used by
297  this module, and also which symbols are imported. The return value is
298  a dictionary with the used module name as key, and a set of all
299  imported symbol names as value.
300 
301  :returns: a dictionary that gives for each module name the set \
302  of symbols imported from it.
303  :rtype: dict[str, set[str]]
304 
305  '''
306  if self._used_symbols_from_module is None:
307  self._extract_import_information()
308 
309  return self._used_symbols_from_module
310 

References psyclone.parse.module_info.ModuleInfo._extract_import_information(), and psyclone.parse.module_info.ModuleInfo._used_symbols_from_module.

Here is the call graph for this function:

◆ name()

◆ resolve_routine()

def psyclone.parse.module_info.ModuleInfo.resolve_routine (   self,
  routine_name 
)
This function returns a list of function names that might be
actually called when the routine `name` is called. In most cases
this is exactly name, but in case of a generic subroutine the
name might change. For now (since we cannot resolve generic
interfaces yet), we return the list of all possible functions that
might be called.

:param str routine_name: the name of the routine to resolve

:returns: list of routine name(s) that could be called.
:rtype: list[str]

Definition at line 354 of file module_info.py.

354  def resolve_routine(self, routine_name):
355  '''This function returns a list of function names that might be
356  actually called when the routine `name` is called. In most cases
357  this is exactly name, but in case of a generic subroutine the
358  name might change. For now (since we cannot resolve generic
359  interfaces yet), we return the list of all possible functions that
360  might be called.
361 
362  :param str routine_name: the name of the routine to resolve
363 
364  :returns: list of routine name(s) that could be called.
365  :rtype: list[str]
366 
367  '''
368  # TODO #2422: once #2422 is done, this can be moved into the PSyIR
369  if self._psyir is None:
370  self.get_psyir()
371  routine_name = routine_name.lower()
372  if routine_name not in self._generic_interfaces:
373  return [routine_name]
374 
375  # If a generic interface name is queried, return a copy
376  # of all possible routine names that might be called:
377  return self._generic_interfaces[routine_name][:]

References psyclone.parse.module_info.ModuleInfo._generic_interfaces, psyclone.parse.module_info.ModuleInfo._psyir, and psyclone.parse.module_info.ModuleInfo.get_psyir().

Here is the call graph for this function:

The documentation for this class was generated from the following file: