Skip to content

Commit 994e0ba

Browse files
committed
Moved the code that generates .txt and .html draft files from .xml to a place where potential errors can be caught and displayed better. Related to ticket ietf-tools#2814.
- Legacy-Id: 16862
1 parent 2d9ff6c commit 994e0ba

4 files changed

Lines changed: 106 additions & 75 deletions

File tree

ietf/submit/forms.py

Lines changed: 102 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@
1111
import email
1212
import pytz
1313
import six
14+
import sys
1415
import tempfile
1516
import xml2rfc
1617

1718
from email.utils import formataddr
1819
from unidecode import unidecode
1920

21+
if six.PY2:
22+
from StringIO import StringIO
23+
else:
24+
from io import StringIO
25+
26+
2027
from django import forms
2128
from django.conf import settings
22-
from django.utils.html import mark_safe
29+
from django.utils.html import mark_safe # type:ignore
2330
from django.urls import reverse as urlreverse
2431
from django.utils.encoding import force_str
2532

@@ -39,6 +46,7 @@
3946
from ietf.submit.parsers.plain_parser import PlainParser
4047
from ietf.submit.parsers.ps_parser import PSParser
4148
from ietf.submit.parsers.xml_parser import XMLParser
49+
from ietf.utils import log
4250
from ietf.utils.draft import Draft
4351

4452
class SubmissionBaseUploadForm(forms.Form):
@@ -128,6 +136,20 @@ def clean_xml(self):
128136
return self.clean_file("xml", XMLParser)
129137

130138
def clean(self):
139+
def format_messages(where, e, log):
140+
out = log.write_out.getvalue().splitlines()
141+
err = log.write_err.getvalue().splitlines()
142+
m = str(e)
143+
if m:
144+
m = [ m ]
145+
else:
146+
import traceback
147+
typ, val, tb = sys.exc_info()
148+
m = traceback.format_exception(typ, val, tb)
149+
m = [ l.replace('\n ', ':\n ') for l in m ]
150+
msgs = [s for s in (["Error from xml2rfc (%s):" % (where,)] + m + out + err) if s]
151+
return msgs
152+
131153
if self.shutdown and not has_role(self.request.user, "Secretariat"):
132154
raise forms.ValidationError('The submission tool is currently shut down')
133155

@@ -145,6 +167,9 @@ def clean(self):
145167
xml_file = self.cleaned_data.get('xml')
146168
name, ext = os.path.splitext(os.path.basename(xml_file.name))
147169
tfh, tfn = tempfile.mkstemp(prefix=name+'-', suffix='.xml')
170+
file_name = {}
171+
xml2rfc.log.write_out = StringIO() # open(os.devnull, "w")
172+
xml2rfc.log.write_err = StringIO() # open(os.devnull, "w")
148173
try:
149174
# We need to write the xml file to disk in order to hand it
150175
# over to the xml parser. XXX FIXME: investigate updating
@@ -154,33 +179,15 @@ def clean(self):
154179
for chunk in xml_file.chunks():
155180
tf.write(chunk)
156181
os.environ["XML_LIBRARY"] = settings.XML_LIBRARY
182+
# --- Parse the xml ---
157183
try:
158184
parser = xml2rfc.XmlRfcParser(str(tfn), quiet=True)
159185
self.xmltree = parser.parse(normalize=True)
160-
root = self.xmltree.getroot()
161-
ver = root.get('version', '2')
162-
if ver == '2':
163-
ok, errors = self.xmltree.validate()
164-
else:
165-
# XXX TODO: Add v3 validation
166-
ok, errors = True, ''
167-
except Exception as exc:
168-
raise forms.ValidationError("An exception occurred when trying to process the XML file: %s" % exc)
169-
if not ok:
170-
# Each error has properties:
171-
#
172-
# message: the message text
173-
# domain: the domain ID (see lxml.etree.ErrorDomains)
174-
# type: the message type ID (see lxml.etree.ErrorTypes)
175-
# level: the log level ID (see lxml.etree.ErrorLevels)
176-
# line: the line at which the message originated (if applicable)
177-
# column: the character column at which the message originated (if applicable)
178-
# filename: the name of the file in which the message originated (if applicable)
179-
raise forms.ValidationError(
180-
[ forms.ValidationError("One or more XML validation errors occurred when processing the XML file:") ] +
181-
[ forms.ValidationError("%s: Line %s: %s" % (xml_file.name, e.line, e.message), code="%s"%e.type) for e in errors ]
182-
)
183-
self.xmlroot = self.xmltree.getroot()
186+
self.xmlroot = self.xmltree.getroot()
187+
xml_version = self.xmlroot.get('version', '2')
188+
except Exception as e:
189+
raise forms.ValidationError("An exception occurred when trying to [arse the XML file: %s" % e)
190+
184191
draftname = self.xmlroot.attrib.get('docName')
185192
if draftname is None:
186193
raise forms.ValidationError("No docName attribute found in the xml root element")
@@ -212,8 +219,76 @@ def clean(self):
212219
if info[item]:
213220
info[item] = info[item].strip()
214221
self.authors.append(info)
215-
except forms.ValidationError:
216-
raise
222+
223+
# --- Prep the xml ---
224+
file_name['xml'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.%s' % (self.filename, self.revision, ext))
225+
try:
226+
if xml_version == '3':
227+
prep = xml2rfc.PrepToolWriter(self.xmltree, quiet=True)
228+
self.xmltree.tree = prep.prep()
229+
if self.xmltree.tree == None:
230+
raise forms.ValidationError("Error from xml2rfc (prep): %s" % prep.errors)
231+
except Exception as e:
232+
msgs = format_messages('prep', e, xml2rfc.log)
233+
raise forms.ValidationError(msgs)
234+
235+
# --- Convert to txt ---
236+
if not ('txt' in self.cleaned_data and self.cleaned_data['txt']):
237+
file_name['txt'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (self.filename, self.revision))
238+
try:
239+
if xml_version != '3':
240+
pagedwriter = xml2rfc.PaginatedTextRfcWriter(self.xmltree, quiet=True)
241+
pagedwriter.write(file_name['txt'])
242+
else:
243+
writer = xml2rfc.TextWriter(self.xmltree, quiet=True)
244+
writer.write(file_name['txt'])
245+
log.log("In %s: xml2rfc %s generated %s from %s (version %s)" %
246+
( os.path.dirname(file_name['xml']),
247+
xml2rfc.__version__,
248+
os.path.basename(file_name['txt']),
249+
os.path.basename(file_name['xml']),
250+
xml_version))
251+
except Exception as e:
252+
msgs = format_messages('txt', e, xml2rfc.log)
253+
raise forms.ValidationError(msgs)
254+
255+
# --- Convert to xml ---
256+
if xml_version == '3':
257+
try:
258+
file_name['html'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.html' % (self.filename, self.revision))
259+
writer = xml2rfc.HtmlWriter(self.xmltree, quiet=True)
260+
writer.write(file_name['html'])
261+
self.file_types.append('.html')
262+
log.log("In %s: xml2rfc %s generated %s from %s (version %s)" %
263+
( os.path.dirname(file_name['xml']),
264+
xml2rfc.__version__,
265+
os.path.basename(file_name['html']),
266+
os.path.basename(file_name['xml']),
267+
xml_version))
268+
except Exception as e:
269+
msgs = format_messages('html', e, xml2rfc.log)
270+
raise forms.ValidationError(msgs)
271+
272+
if xml_version == '2':
273+
ok, errors = self.xmltree.validate()
274+
else:
275+
ok, errors = True, ''
276+
277+
if not ok:
278+
# Each error has properties:
279+
#
280+
# message: the message text
281+
# domain: the domain ID (see lxml.etree.ErrorDomains)
282+
# type: the message type ID (see lxml.etree.ErrorTypes)
283+
# level: the log level ID (see lxml.etree.ErrorLevels)
284+
# line: the line at which the message originated (if applicable)
285+
# column: the character column at which the message originated (if applicable)
286+
# filename: the name of the file in which the message originated (if applicable)
287+
raise forms.ValidationError(
288+
[ forms.ValidationError("One or more XML validation errors occurred when processing the XML file:") ] +
289+
[ forms.ValidationError("%s: Line %s: %s" % (xml_file.name, r.line, r.message), code="%s"%r.type) for r in errors ]
290+
)
291+
217292
finally:
218293
os.close(tfh)
219294
os.unlink(tfn)

ietf/submit/templatetags/submit_tags.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from django import template
1010
from django.conf import settings
11-
from django.utils.html import mark_safe, escape
11+
from django.utils.html import mark_safe, escape # type:ignore
1212

1313
register = template.Library()
1414

ietf/submit/tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@ def test_api_submit_pending_submission(self):
17741774
self.assertContains(r, expected, status_code=400)
17751775

17761776
def test_api_submit_no_title(self):
1777-
r, author, name = self.post_submission('00', title="")
1777+
r, author, name = self.post_submission('00', title=" ")
17781778
expected = "Could not extract a valid title from the upload"
17791779
self.assertContains(r, expected, status_code=400)
17801780

ietf/submit/utils.py

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import os
1010
import re
1111
import six # pyflakes:ignore
12-
import xml2rfc
1312
if six.PY3:
1413
from typing import Callable, Optional # pyflakes:ignore
1514

@@ -649,57 +648,14 @@ def get_draft_meta(form, saved_files):
649648
file_name = saved_files
650649
abstract = None
651650
file_size = None
652-
xml2rfc.log.write_out = open(os.devnull, "w")
653-
xml2rfc.log.write_err = open(os.devnull, "w")
654651
if form.cleaned_data['xml']:
655-
try:
656-
xmlroot = form.xmltree.getroot()
657-
xml_version = xmlroot.get('version', '2')
658-
if xml_version == '3':
659-
prep = xml2rfc.PrepToolWriter(form.xmltree, quiet=True)
660-
form.xmltree.tree = prep.prep()
661-
if form.xmltree.tree == None:
662-
raise ValidationError("Error from xml2rfc (prep): %s" % prep.errors)
663-
except Exception as e:
664-
raise ValidationError("Error from xml2rfc (prep): %s" % e)
665-
if not ('txt' in form.cleaned_data and form.cleaned_data['txt']):
666-
file_name['txt'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (form.filename, form.revision))
667-
try:
668-
if xml_version != '3':
669-
pagedwriter = xml2rfc.PaginatedTextRfcWriter(form.xmltree, quiet=True)
670-
pagedwriter.write(file_name['txt'])
671-
else:
672-
writer = xml2rfc.TextWriter(form.xmltree, quiet=True)
673-
writer.write(file_name['txt'])
674-
log.log("In %s: xml2rfc %s generated %s from %s (version %s)" %
675-
( os.path.dirname(file_name['xml']),
676-
xml2rfc.__version__,
677-
os.path.basename(file_name['txt']),
678-
os.path.basename(file_name['xml']),
679-
xml_version))
680-
except Exception as e:
681-
raise ValidationError("Error from xml2rfc (text): %s" % e)
682-
file_size = os.stat(file_name['txt']).st_size
683-
if xml_version == '3':
684-
try:
685-
file_name['html'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.html' % (form.filename, form.revision))
686-
writer = xml2rfc.HtmlWriter(form.xmltree, quiet=True)
687-
writer.write(file_name['html'])
688-
form.file_types.append('.html')
689-
log.log("In %s: xml2rfc %s generated %s from %s (version %s)" %
690-
( os.path.dirname(file_name['xml']),
691-
xml2rfc.__version__,
692-
os.path.basename(file_name['html']),
693-
os.path.basename(file_name['xml']),
694-
xml_version))
695-
except Exception as e:
696-
raise ValidationError("Error from xml2rfc (html): %s" % e)
697652
# Some meta-information, such as the page-count, can only
698653
# be retrieved from the generated text file. Provide a
699654
# parsed draft object to get at that kind of information.
655+
file_name['txt'] = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (form.filename, form.revision))
656+
file_size = os.stat(file_name['txt']).st_size
700657
with io.open(file_name['txt']) as txt_file:
701658
form.parsed_draft = Draft(txt_file.read(), txt_file.name)
702-
703659
else:
704660
file_size = form.cleaned_data['txt'].size
705661

0 commit comments

Comments
 (0)