@@ -18,10 +18,14 @@
import re
from collections import OrderedDict
-from .error import QAPIParseError, QAPISemError
+from .error import QAPIError, QAPIParseError, QAPISemError
from .source import QAPISourceInfo
+class QAPIDocError(QAPIError):
+ """Documentation parsing error."""
+
+
class QAPISchemaParser:
def __init__(self, fname, previously_included=None, incl_info=None):
@@ -265,13 +269,13 @@ def get_expr(self, nested):
self, "expected '{', '[', string, boolean or 'null'")
return expr
- def get_doc(self, info):
+ def _get_doc(self, info):
if self.val != '##':
raise QAPIParseError(
self, "junk after '##' at start of documentation comment")
docs = []
- cur_doc = QAPIDoc(self, info)
+ cur_doc = QAPIDoc(info)
self.accept(False)
while self.tok == '#':
if self.val.startswith('##'):
@@ -292,12 +296,22 @@ def get_doc(self, info):
if cur_doc.body.text:
cur_doc.end_comment()
docs.append(cur_doc)
- cur_doc = QAPIDoc(self, info)
+ cur_doc = QAPIDoc(info)
cur_doc.append(self.val)
self.accept(False)
raise QAPIParseError(self, "documentation comment must end with '##'")
+ def get_doc(self, info):
+ try:
+ return self._get_doc(info)
+ except QAPIDocError as err:
+ # Tie the Doc parsing error to our parsing state. The
+ # resulting error position depends on the state of the
+ # parser. It happens to be the beginning of the comment.
+ # More or less servicable, but action at a distance.
+ raise QAPIParseError(self, str(err)) from err
+
class QAPIDoc:
"""
@@ -335,12 +349,7 @@ def __init__(self, name):
def connect(self, member):
self.member = member
- def __init__(self, parser, info):
- # self._parser is used to report errors with QAPIParseError. The
- # resulting error position depends on the state of the parser.
- # It happens to be the beginning of the comment. More or less
- # servicable, but action at a distance.
- self._parser = parser
+ def __init__(self, info):
self.info = info
self.symbol = None
self.body = QAPIDoc.Section()
@@ -377,7 +386,7 @@ def append(self, line):
return
if line[0] != ' ':
- raise QAPIParseError(self._parser, "missing space after #")
+ raise QAPIDocError("missing space after #")
line = line[1:]
self._append_line(line)
@@ -411,11 +420,11 @@ def _append_body_line(self, line):
# recognized, and get silently treated as ordinary text
if not self.symbol and not self.body.text and line.startswith('@'):
if not line.endswith(':'):
- raise QAPIParseError(self._parser, "line should end with ':'")
+ raise QAPIDocError("line should end with ':'")
self.symbol = line[1:-1]
# FIXME invalid names other than the empty string aren't flagged
if not self.symbol:
- raise QAPIParseError(self._parser, "invalid name")
+ raise QAPIDocError("invalid name")
elif self.symbol:
# This is a definition documentation block
if name.startswith('@') and name.endswith(':'):
@@ -498,9 +507,8 @@ def _append_various_line(self, line):
name = line.split(' ', 1)[0]
if name.startswith('@') and name.endswith(':'):
- raise QAPIParseError(self._parser,
- "'%s' can't follow '%s' section"
- % (name, self.sections[0].name))
+ raise QAPIDocError("'%s' can't follow '%s' section"
+ % (name, self.sections[0].name))
if self._is_section_tag(name):
line = line[len(name)+1:]
self._start_section(name[:-1])
@@ -514,10 +522,9 @@ def _append_various_line(self, line):
def _start_symbol_section(self, symbols_dict, name):
# FIXME invalid names other than the empty string aren't flagged
if not name:
- raise QAPIParseError(self._parser, "invalid parameter name")
+ raise QAPIDocError("invalid parameter name")
if name in symbols_dict:
- raise QAPIParseError(self._parser,
- "'%s' parameter name duplicated" % name)
+ raise QAPIDocError("'%s' parameter name duplicated" % name)
assert not self.sections
self._end_section()
self._section = QAPIDoc.ArgSection(name)
@@ -531,8 +538,7 @@ def _start_features_section(self, name):
def _start_section(self, name=None):
if name in ('Returns', 'Since') and self.has_section(name):
- raise QAPIParseError(self._parser,
- "duplicated '%s' section" % name)
+ raise QAPIDocError("duplicated '%s' section" % name)
self._end_section()
self._section = QAPIDoc.Section(name)
self.sections.append(self._section)
@@ -541,17 +547,15 @@ def _end_section(self):
if self._section:
text = self._section.text = self._section.text.strip()
if self._section.name and (not text or text.isspace()):
- raise QAPIParseError(
- self._parser,
+ raise QAPIDocError(
"empty doc section '%s'" % self._section.name)
self._section = None
def _append_freeform(self, line):
match = re.match(r'(@\S+:)', line)
if match:
- raise QAPIParseError(self._parser,
- "'%s' not allowed in free-form documentation"
- % match.group(1))
+ raise QAPIDocError("'%s' not allowed in free-form documentation"
+ % match.group(1))
self._section.append(line)
def connect_member(self, member):
We only need the parser context for the purpose of raising exceptions. What we can do instead is to raise a unique document parsing error and affix additional context from the caller. This way, the document parsing can be isolated. Signed-off-by: John Snow <jsnow@redhat.com> --- scripts/qapi/parser.py | 56 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-)