Add logging to parser.py

This patch adds logging to parser.py. All log entries are written to a
log file. Log entries at a level higher than debug level are also
written to the console.

The patch uses logging to make it easy to diagnose parsing errors caused
by mismatched start and end markers for distro and code blocks.

Change-Id: Ieea40d58aaf827ef9ba2e4eec75c450c5a58ea27
This commit is contained in:
Roger Luethi 2016-12-29 18:32:51 +01:00
parent aba1cef0ec
commit 68b8c03838
1 changed files with 97 additions and 4 deletions

View File

@ -12,6 +12,7 @@
from collections import defaultdict
import logging
import os
import re
import yaml
@ -20,6 +21,41 @@ import yaml
import parsererr as ParserErr
def configure_logging():
"""Configure root logger"""
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# Level name colored differently (both console and file)
logging.addLevelName(logging.WARNING, '\x1b[0;33m%s\x1b[0m' %
logging.getLevelName(logging.WARNING))
logging.addLevelName(logging.ERROR, '\x1b[0;31m%s\x1b[0m' %
logging.getLevelName(logging.ERROR))
# Configure console logging
console_log_handler = logging.StreamHandler()
console_log_handler.setLevel(logging.INFO)
# All console messages are the same color (except with colored level names)
console_formatter = logging.Formatter('\x1b[0;32m%(levelname)s'
'\t%(message)s\x1b[0m')
console_log_handler.setFormatter(console_formatter)
logger.addHandler(console_log_handler)
# Configure log file
log_file = 'rst2bash.log'
os.remove(log_file)
file_log_handler = logging.FileHandler(log_file)
file_log_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(process)s %(asctime)s.%(msecs)03d'
' %(name)s %(levelname)s %(message)s',
datefmt="%H:%M:%S")
file_log_handler.setFormatter(file_formatter)
logger.addHandler(file_log_handler)
logger.debug("Root logger configured.")
# TODO(dbite): Remove CamelCasing.
# ------------------------------------------------------------------------------
# Custom data-types.
@ -347,20 +383,28 @@ class ExtractBlocks(object):
def __init__(self, rstFile, bashPath):
logger.info("Processing %s.", os.path.basename(rstFile))
self.rstFile = self.get_file_contents(rstFile)
self.blocks = None # Lookup table.
self.allBlocksIterator = None
self.parseblocks = ParseBlocks()
self.bashCode = list()
bashFileName = os.path.basename(rstFile).replace('.rst', '.sh')
logger.debug("bashPath %s", bashPath)
self.bashPath = {distro: os.path.join(path, bashFileName)
for distro, path in bashPath.iteritems()}
logger.debug("ExtractBlocks __init__ bashPath %s", self.bashPath)
def __del__(self):
"""Proper handling of the file pointer."""
self.filePointer.close()
def index_to_line_no(self, index):
"""Return line number, given index into string"""
# Count newline characters (no newline -> line number 1)
return self.rstFile.count("\n", 0, index) + 1
def _get_indices(self, regexStr):
"""Helper function to return a tuple containing indices.
@ -372,8 +416,36 @@ class ExtractBlocks(object):
indices = [index.span()
for index in searchBlocks.finditer(self.rstFile)]
logger.debug("_get_indices %s %s", regexStr, indices)
return indices
def get_start_end_block(self, searchStart, searchEnd):
"""Search file for start and stop codes
Search for start and stop codes (e.g., "only", "endonly") and
report an error if the numbers for both don't match.
"""
start = self._get_indices(searchStart)
end = self._get_indices(searchEnd)
# Log information on the indices we received
msg = "get_start_end_block start/end mismatch:\n"
msg += " regex start: {}\n".format(searchStart)
msg += " regex end: {}\n".format(searchEnd)
report = {}
for ii in start:
report[self.index_to_line_no(ii[0])] = "start block"
for ii in end:
report[self.index_to_line_no(ii[0])] = "end block "
for ii in sorted(report):
msg += " {} (line {})\n".format(report[ii], ii)
if len(start) == len(end):
logger.debug(msg)
else:
logger.error(msg)
return start, end
def get_file_contents(self, filePath):
"""Return the contents of the given file."""
@ -408,10 +480,15 @@ class ExtractBlocks(object):
searchPath = '''\.\.\spath\s.*''' # Look for .. path
allBlocks = BlockIndex(self._get_indices(searchAllBlocks))
distroBlocks = BlockIndex(self._get_indices(searchDistroBlocksStart),
self._get_indices(searchDistroBlocksEnd))
codeBlocks = BlockIndex(self._get_indices(searchCodeBlocksStart),
self._get_indices(searchCodeBlocksEnd))
startIndex, endIndex = self.get_start_end_block(
searchDistroBlocksStart, searchDistroBlocksEnd)
distroBlocks = BlockIndex(startIndex, endIndex)
startIndex, endIndex = self.get_start_end_block(
searchCodeBlocksStart, searchCodeBlocksEnd)
codeBlocks = BlockIndex(startIndex, endIndex)
pathBlocks = BlockIndex(self._get_indices(searchPath))
# Point to the blocks from a dictionary to create sensible index.
@ -556,15 +633,25 @@ class ExtractBlocks(object):
if __name__ == '__main__':
configure_logging()
logger = logging.getLogger()
# TODO(dbite): Cleanup the main function.
with open("rst2bash/config/parser_config.yaml", 'r') as ymlfile:
cfg = yaml.load(ymlfile)
cwd = os.getcwd()
logger.debug("cwd %s", cwd)
rst_path = os.path.join(cwd, cfg['rst_path'])
logger.debug("rst_path %s", rst_path)
rst_files = cfg['rst_files']
logger.debug("rst_files %s", rst_files)
bash_path = {distro: os.path.join(cwd, path)
for distro, path in cfg['bash_path'].iteritems()}
logger.debug("bash_path %s", bash_path)
for path_value in bash_path.itervalues():
@ -591,9 +678,15 @@ if __name__ == '__main__':
ParserErr.InvalidBlockError,
ParserErr.MissingTagsError) as ex:
parser_message = parser_message + repr(ex) + "\n"
logger.error(repr(ex))
except ParserErr.Rst2BashError as ex:
pass
else:
parser_message = "Success :): parsed %s to bash. :D"
finally:
print(parser_message % rst_file)
logger.info("")
logger.info("Output written to:")
for distro in bash_path:
logger.info(bash_path[distro])