Skip to content

Commit

Permalink
feat: Validate docName and seriesInfo value for I-D (#1116)
Browse files Browse the repository at this point in the history
* refactor: Remove redundant import

* refactor: Remove an obsolete warning

* feat: Validate docName and seriesInfo value for I-D

Fixes #1115
  • Loading branch information
kesara authored Mar 21, 2024
1 parent 312b01a commit ca39326
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 19 deletions.
77 changes: 76 additions & 1 deletion test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from xml2rfc.boilerplate_rfc_7841 import boilerplate_rfc_status_of_memo
from xml2rfc.walkpdf import xmldoc
from xml2rfc.writers.base import default_options
from xml2rfc.writers.base import default_options, BaseV3Writer, RfcWriterError
from xml2rfc.writers.text import MAX_WIDTH

try:
Expand Down Expand Up @@ -697,6 +697,81 @@ def test_render_reference(self):
self.assertEqual(len(lines), 2)
self.assertIn(url, lines[1].text)

class BaseV3WriterTest(unittest.TestCase):
'''BaseV3Writer tests'''

def setUp(self):
xml2rfc.log.quiet = True
path = 'tests/input/elements.xml'
self.parser = xml2rfc.XmlRfcParser(path,
quiet=True,
options=default_options,
**options_for_xmlrfcparser)
self.xmlrfc = self.parser.parse()
self.writer = BaseV3Writer(self.xmlrfc, quiet=True)

def test_validate_draft_name(self):
# Valid documents
valid_docs = []
valid_docs.append(lxml.etree.fromstring('''
<rfc
number="9280"
docName = "draft-ietf-foo-bar-23"
ipr="trust200902"
submissionType="editorial"
category="info">
<link href="https://datatracker.ietf.org/doc/draft-ietf-foo-bar-23" rel="prev"/>
<front>
<seriesInfo name="RFC" value="9280" stream="IETF" />
</front>
</rfc>'''))
valid_docs.append(lxml.etree.fromstring('''
<rfc
docName = "draft-ietf-foo-bar-23"
ipr="trust200902"
submissionType="editorial"
category="info">
<front>
<seriesInfo name="Internet-Draft" value="draft-ietf-foo-bar-23" />
</front>
</rfc>'''))
valid_docs.append(lxml.etree.fromstring('''
<rfc
docName = "draft-ietf-foo-bar-23"
ipr="trust200902"
submissionType="editorial"
category="info">
<front>
</front>
</rfc>'''))
valid_docs.append(lxml.etree.fromstring('''
<rfc
ipr="trust200902"
submissionType="editorial"
category="info">
<front>
<seriesInfo name="Internet-Draft" value="draft-ietf-foo-bar-23" />
</front>
</rfc>'''))
for valid_doc in valid_docs:
self.writer.root = valid_doc
self.assertTrue(self.writer.validate_draft_name())

# Invalid document
invalid_doc = lxml.etree.fromstring('''
<rfc
docName = "draft-ietf-foo-bar-23"
ipr="trust200902"
submissionType="editorial"
category="info">
<front>
<seriesInfo name="Internet-Draft" value="draft-ietf-foo-bar-3" />
</front>
</rfc>''')
self.writer.root = invalid_doc
with self.assertRaises(RfcWriterError):
self.writer.validate_draft_name()


if __name__ == '__main__':
unittest.main()
41 changes: 23 additions & 18 deletions xml2rfc/writers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import copy
import datetime
import textwrap
import lxml
import os
import re
import xml2rfc.log
Expand Down Expand Up @@ -1057,7 +1056,7 @@ def write_section_rec(self, section, count_str="1.", appendix=False,
p_count = 1 # Paragraph counter
for element in section:
# Check for a PI
if element.tag is lxml.etree.PI:
if element.tag is etree.PI:
pidict = self.parse_pi(element)
if pidict and "needLines" in pidict:
self.needLines(pidict["needLines"])
Expand Down Expand Up @@ -1297,7 +1296,7 @@ def _build_index(self):
if 'anchor' in ref.attrib:
self._indexRef(ref_counter, title=title.text, anchor=ref.attrib["anchor"])
else:
raise RfcWriterError("Reference is missing an anchor: %s" % lxml.etree.tostring(ref))
raise RfcWriterError("Reference is missing an anchor: %s" % etree.tostring(ref))

# Appendix sections
back = self.r.find('back')
Expand Down Expand Up @@ -1646,7 +1645,7 @@ def write_to_file(self, file):

v3_rnc_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'v3.rnc')
v3_rng_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'v3.rng')
v3_schema = lxml.etree.ElementTree(file=v3_rng_file)
v3_schema = etree.ElementTree(file=v3_rng_file)

def get_element_tags():
tags = set()
Expand Down Expand Up @@ -1733,7 +1732,7 @@ def __init__(self, xmlrfc, quiet=None, options=default_options, date=None):
self.date = date if date is not None else datetime.date.today()
self.v3_rnc_file = v3_rnc_file
self.v3_rng_file = v3_rng_file
self.v3_rng = lxml.etree.RelaxNG(file=self.v3_rng_file)
self.v3_rng = etree.RelaxNG(file=self.v3_rng_file)
self.v3_schema = v3_schema
self.schema = v3_schema
self.index_items = []
Expand Down Expand Up @@ -1780,21 +1779,21 @@ def get_relevant_pis(self, e):
if e != None:
# directly inside element
for c in e.getchildren():
if c.tag == lxml.etree.PI and c.target == xml2rfc.V3_PI_TARGET:
if c.tag == etree.PI and c.target == xml2rfc.V3_PI_TARGET:
pis.append(c)
# siblings before element
for s in e.itersiblings(preceding=True):
if s.tag == lxml.etree.PI and s.target == xml2rfc.V3_PI_TARGET:
if s.tag == etree.PI and s.target == xml2rfc.V3_PI_TARGET:
pis.append(s)
# ancestor's earlier siblings
for a in e.iterancestors():
for s in a.itersiblings(preceding=True):
if s.tag == lxml.etree.PI and s.target == xml2rfc.V3_PI_TARGET:
if s.tag == etree.PI and s.target == xml2rfc.V3_PI_TARGET:
pis.append(s)
# before root elements
p = self.root.getprevious()
while p != None:
if p.tag == lxml.etree.PI and p.target == xml2rfc.V3_PI_TARGET:
if p.tag == etree.PI and p.target == xml2rfc.V3_PI_TARGET:
pis.append(p)
p = p.getprevious()
return pis
Expand Down Expand Up @@ -2045,9 +2044,9 @@ def pretty_print_prep(self, e, p):
ind = self.options.indent
## The actual printing is done in self.write()
def indent(e, i):
if e.tag in (lxml.etree.CDATA, ):
if e.tag in (etree.CDATA, ):
return
if e.tag in (lxml.etree.Comment, lxml.etree.PI, ):
if e.tag in (etree.Comment, etree.PI, ):
if not e.tail:
if e.getnext() != None:
e.tail = '\n'+' '*i
Expand Down Expand Up @@ -2128,13 +2127,6 @@ def validate(self, when='', warn=False):
self.v3_rng.assertValid(tree)
return True
except Exception as e:
lxmlver = lxml.etree.LXML_VERSION[:3]
if lxmlver < (3, 8, 0):
self.warn(None, "The available version of the lxml library (%s) does not provide xpath "
"information as part of validation errors. Upgrade to version 3.8.0 or "
"higher for better error messages." % ('.'.join(str(v) for v in lxmlver), ))
# These warnings are occasionally incorrect -- disable this
# output for now:
deadly = False
if hasattr(e, 'error_log'):
for error in e.error_log:
Expand Down Expand Up @@ -2166,6 +2158,19 @@ def validate_before(self, e, p):
if not self.validate('before'):
self.note(None, "Schema validation failed for input document")

self.validate_draft_name()

def validate_draft_name(self):
if not self.root.attrib.get('number', False):
docName = self.root.attrib.get('docName', None)
info = self.root.find('./front/seriesInfo[@name="Internet-Draft"]')
si_draft_name = info.get('value') if info != None else None

if all([docName, si_draft_name]) and docName != si_draft_name:
self.die(self.root, 'docName and value in <seriesInfo name="Internet-Draft" ..> must match.')

return True

def validate_after(self, e, p):
# XXX: There is an issue with exponential increase in validation time
# as a function of the number of attributes on the root element, on
Expand Down

0 comments on commit ca39326

Please sign in to comment.