qapi: Improve source file read error handling

qapi-gen.py crashes when it can't open the main schema file, and when
it can't read from any schema file.  Lazy.

Change QAPISchema.__init__() to take a file name instead of a file
object.  Move the open code from _include() to __init__(), so it's
used for the main schema file, too.

Move the read into the try for good measure, and rephrase the error
message.

Reporting open or read failure for the main schema file needs a
QAPISourceInfo representing "no source".  Make QAPISourceInfo cope
with fname=None.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20190927134639.4284-27-armbru@redhat.com>
This commit is contained in:
Markus Armbruster 2019-09-27 15:46:39 +02:00
parent 56d2df5e65
commit c615550df3
2 changed files with 27 additions and 21 deletions

View File

@ -53,7 +53,12 @@ class QAPISourceInfo(object):
return info return info
def loc(self): def loc(self):
return '%s:%d' % (self.fname, self.line) if self.fname is None:
return sys.argv[0]
ret = self.fname
if self.line is not None:
ret += ':%d' % self.line
return ret
def in_defn(self): def in_defn(self):
if self.defn_name: if self.defn_name:
@ -383,14 +388,26 @@ class QAPIDoc(object):
class QAPISchemaParser(object): class QAPISchemaParser(object):
def __init__(self, fp, previously_included=[], incl_info=None): def __init__(self, fname, previously_included=[], incl_info=None):
self.fname = fp.name previously_included.append(os.path.abspath(fname))
previously_included.append(os.path.abspath(fp.name))
self.src = fp.read() try:
if sys.version_info[0] >= 3:
fp = open(fname, 'r', encoding='utf-8')
else:
fp = open(fname, 'r')
self.src = fp.read()
except IOError as e:
raise QAPISemError(incl_info or QAPISourceInfo(None, None, None),
"can't read %s file '%s': %s"
% ("include" if incl_info else "schema",
fname,
e.strerror))
if self.src == '' or self.src[-1] != '\n': if self.src == '' or self.src[-1] != '\n':
self.src += '\n' self.src += '\n'
self.cursor = 0 self.cursor = 0
self.info = QAPISourceInfo(self.fname, 1, incl_info) self.info = QAPISourceInfo(fname, 1, incl_info)
self.line_pos = 0 self.line_pos = 0
self.exprs = [] self.exprs = []
self.docs = [] self.docs = []
@ -414,7 +431,7 @@ class QAPISchemaParser(object):
if not isinstance(include, str): if not isinstance(include, str):
raise QAPISemError(info, raise QAPISemError(info,
"value of 'include' must be a string") "value of 'include' must be a string")
incl_fname = os.path.join(os.path.dirname(self.fname), incl_fname = os.path.join(os.path.dirname(fname),
include) include)
self.exprs.append({'expr': {'include': incl_fname}, self.exprs.append({'expr': {'include': incl_fname},
'info': info}) 'info': info})
@ -466,14 +483,7 @@ class QAPISchemaParser(object):
if incl_abs_fname in previously_included: if incl_abs_fname in previously_included:
return None return None
try: return QAPISchemaParser(incl_fname, previously_included, info)
if sys.version_info[0] >= 3:
fobj = open(incl_fname, 'r', encoding='utf-8')
else:
fobj = open(incl_fname, 'r')
except IOError as e:
raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
return QAPISchemaParser(fobj, previously_included, info)
def _pragma(self, name, value, info): def _pragma(self, name, value, info):
global doc_required, returns_whitelist, name_case_whitelist global doc_required, returns_whitelist, name_case_whitelist
@ -1734,11 +1744,7 @@ class QAPISchemaEvent(QAPISchemaEntity):
class QAPISchema(object): class QAPISchema(object):
def __init__(self, fname): def __init__(self, fname):
self.fname = fname self.fname = fname
if sys.version_info[0] >= 3: parser = QAPISchemaParser(fname)
f = open(fname, 'r', encoding='utf-8')
else:
f = open(fname, 'r')
parser = QAPISchemaParser(f)
exprs = check_exprs(parser.exprs) exprs = check_exprs(parser.exprs)
self.docs = parser.docs self.docs = parser.docs
self._entity_list = [] self._entity_list = []

View File

@ -1 +1 @@
tests/qapi-schema/include-no-file.json:1: No such file or directory: tests/qapi-schema/include-no-file-sub.json tests/qapi-schema/include-no-file.json:1: can't read include file 'tests/qapi-schema/include-no-file-sub.json': No such file or directory