parser: Improve exception handling

Multiple improvements for exception handling and raising the correct
exceptions. The following changes make the output nicer for readability
although, it still has some bits missing.

- Renaming exceptions.py to parsererr.py due to H237.
- Changing the custom exception prefix from Exception to Error.
- Parser deliberately raises custom exceptions pointing out the error.
- Changes to custom exception classes to match the error type better.
- Printing the rst_file name with a nicer error output.
- Exact location in the rst file needs find or grep method as the
  location (LOC) is not yet provided.
- Errors are still easy to trace with find or grep.

Change-Id: I008563d7caaf4d059b30d7b687faf823b2e6a913
This commit is contained in:
Pranav Salunke 2016-12-28 14:54:12 +01:00
parent c6d99ea667
commit d23f101054
3 changed files with 94 additions and 54 deletions

View File

@ -1,41 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
class Rst2BashException(Exception):
pass
class MissingTagsException(Rst2BashException):
pass
class NestedDistroBlocksException(Rst2BashException):
pass
class PathNotFoundException(Rst2BashException):
pass
class NoCodeBlocksException(Rst2BashException):
pass
class InvalidOperatorException(Rst2BashException):
pass

View File

@ -17,6 +17,9 @@ import re
import yaml
import parsererr as ParserErr
class BlockIndex(object):
"""Creates indices which describes the location of blocks in rst file.
@ -228,7 +231,8 @@ class ParseBlocks(object):
action = 'config'
codeBlock = self._parse_config(codeBlock)
else:
raise # TODO(dbite): Raise custom exception here.
msg = "Invalid command type: %s" % cmdType
raise ParserErr.InvalidCodeBlockError(msg)
command.append(action=action, command=codeBlock)
@ -289,7 +293,8 @@ class ParseBlocks(object):
elif ">" in operator:
operator = "mysql_exec "
else:
raise # TODO(dbite): Create custom exceptions!
msg = "Invalid operator: %s" % operator
raise ParserErr.InvalidOperatorError(msg)
return operator
@ -408,7 +413,10 @@ class ExtractBlocks(object):
self.allBlocksIterator = \
self.blocks['allBlock'].get_startindex_generator()
self._extractblocks()
try:
self._extractblocks()
except IndexError as err:
raise ParserErr.MissingTagsError(err)
# Helper function for quick lookup from the blocks lookup table.
def _block_lookup(self, allblock):
@ -423,8 +431,8 @@ class ExtractBlocks(object):
if blockIndex is not False:
return blockName, blockIndex
else:
# TODO(dbite): Raise custom exception.
raise
msg = "Invalid block name: %s" % blockName
raise ParserErr.InvalidBlockError(msg)
# Helper function for recursive-generator pattern.
def _extractblocks(self, distro=None, path=None, distroEnd=None):
@ -456,6 +464,11 @@ class ExtractBlocks(object):
block = self.blocks[blockName]
# TODO(dbite): Implement a mechanism for locating the exact location in
# the rst file at the current recursive depth. This
# information should then be logged and passed via. the
# exception traceback. Save required vars. in a global
# variable.
if distroEnd < block.get_start_block(blockIndex)[0]:
distro = None
@ -536,14 +549,27 @@ if __name__ == '__main__':
for rst_file in rst_files:
rst_file_path = os.path.join(rst_path, rst_file)
print("Parsing: %s\n") % (rst_file)
code_blocks = ExtractBlocks(rst_file_path, bash_path)
code_blocks.get_indice_blocks()
parser_message = "\nError: XXX: Failed to parse %s to bash.\n\t - "
try:
rst_file_path = os.path.join(rst_path, rst_file)
code_blocks = ExtractBlocks(rst_file_path, bash_path)
code_blocks.get_indice_blocks()
code_blocks.extract_codeblocks()
except Exception:
bashCode = code_blocks.get_bash_code()
if not code_blocks.write_bash_code():
msg = "Could not write to bash: %s" % rst_file_path
raise ParserErr.Rst2BashError(msg)
except (ParserErr.InvalidCodeBlockError,
ParserErr.InvalidOperatorError,
ParserErr.InvalidBlockError,
ParserErr.MissingTagsError) as ex:
parser_message = parser_message + repr(ex) + "\n"
except ParserErr.Rst2BashError as ex:
pass
bashCode = code_blocks.get_bash_code()
if not code_blocks.write_bash_code():
raise Exception("Could not write to bash")
else:
parser_message = "Success :): parsed %s to bash. :D"
finally:
print(parser_message % rst_file)

55
rst2bash/parsererr.py Normal file
View File

@ -0,0 +1,55 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
class Rst2BashError(Exception):
"""Base class for exceptions for Rst2Bash module."""
pass
class InvalidBlockError(Rst2BashError):
"""Error describing possible sphinx blocks but invalid for rst2bash."""
pass
class MissingTagsError(Rst2BashError):
"""Error describing missing tags, especially rst2bash specific end tags."""
pass
class NestedDistroBlocksError(Rst2BashError):
"""Error describing nested distribution blocks."""
def __init__(self, *args, **kwargs):
Rst2BashError.__init__(self, *args, **kwargs)
class PathNotFoundError(Rst2BashError):
"""Error describing missing path for certain commands (ex: config)."""
def __init__(self, *args, **kwargs):
Rst2BashError.__init__(self, *args, **kwargs)
class InvalidCodeBlockError(Rst2BashError):
"""Error describing unspported code blocks for rst2bash."""
pass
class InvalidOperatorError(Rst2BashError):
"""Error describing bash/db operators which are not supported."""
pass