Source code for bout_runners.make.read_makefile

"""Module containing the classes to read the makefile."""

import logging
import re
from pathlib import Path


[docs]class MakefileReaderError(Exception): """Error class indicating that this is a ReadMakefile error.""" def __init__(self, variable: str, path: Path) -> None: """ Construct a string and call the super constructor. Parameters ---------- variable : str Variable to searched for path : Path or str Path searched at """ message = f"Could not find {variable} in {path}" logging.error(message) super().__init__(message)
[docs]class BoutMakefileReader: """ Class which reads a BOUT++ Makefile. Attributes ---------- path : Path The path to the Makefile content : str The content of the Makefile as a string """ def __init__(self, path: Path) -> None: """ Read the content of a Makefile and store it into self.content. Parameters ---------- path : Path or str Path to the Makefile """ self.path = path self.content = self.read()
[docs] def read(self) -> str: """ Read the makefile. Returns ------- str Content of the Makefile """ with Path(self.path).open("r") as make_file: return make_file.read()
@property def value(self) -> str: """Get the value of the variable.""" return ""
[docs]class BoutMakefileVariableReader(BoutMakefileReader): r""" Class which reads a variable from a BOUT++ Makefile. Attributes ---------- variable_name : str Name of the variable belonging to the instance value : str Value belonging to the variable of the instance Methods ------- get_variable_value() Get the value of the variable Examples -------- Makefile (remember to use TABS instead of SPACES in the Makefile) >>> BOUT_SUPER = /super/path/to/BOUT-dev ... BOUT_TOP = $(BOUT_SUPER)/BOUT-dev ... ... SOURCEC = bout_model.cxx ... ... include $(BOUT_TOP)/make.config Script >>> BoutMakefileVariableReader('SOURCEC', 'Makefile').value 'bout_model.cxx' """ def __init__(self, path: Path, variable_name: str) -> None: """ Set the variable name of the instance. Parameters ---------- path : Path or str The path to the Makefile variable_name : str The variable under consideration """ super().__init__(path) self.variable_name = variable_name @property def value(self) -> str: """ Get the value of the variable. Returns ------- value : str The last match of the variable Raises ------ MakefileReaderError If self.variable is not found Examples -------- Makefile >>> # foo=bar ... foo = baz ... foo = foobar.qux # foo = quux.quuz Script >>> BoutMakefileVariableReader('foo', 'Makefile').value 'foobar.qux' """ # Build the match function for the regex no_comment_line = r"^\s*(?!#)" must_contain_eq_sign = r"\s*=\s*" # As we are using the MULTILINE modifier we should exclude # newlines capture_all_except_comment_and_newline = r"([.]*[^#\n]*)" avoid_trailing_whitespace = r"(?<!\s)" pattern = ( f"{no_comment_line}{self.variable_name}" f"{must_contain_eq_sign}" f"{capture_all_except_comment_and_newline}" f"{avoid_trailing_whitespace}" ) # NOTE: match() checks for match only at the beginning of a # string # search() checks for match anywhere in the string, # but returns only the first occurrence # findall() matches all matching group as a list, # and as we only have one matching group in the # pattern the elements in the list will be string not a # tuple matches = re.findall(pattern, self.content, re.M) if len(matches) == 0: raise MakefileReaderError(self.variable_name, self.path) # Only the last line of the variable will be considered by # the Makefile value: str = matches[-1] return value