def apply(self): if not len(self.document): # @@@ replace these DataErrors with proper system messages raise DataError('Document tree is empty.') header = self.document[0] if not isinstance(header, nodes.field_list) or \ 'rfc2822' not in header['classes']: raise DataError('Document does not begin with an RFC-2822 ' 'header; it is not a PEP.') pep = None for field in header: if field[0].astext().lower() == 'pep': # should be the first field value = field[1].astext() try: pep = int(value) cvs_url = self.pep_cvs_url % pep except ValueError: pep = value cvs_url = None msg = self.document.reporter.warning( '"PEP" header must contain an integer; "%s" is an ' 'invalid value.' % pep, base_node=field) msgid = self.document.set_id(msg) prb = nodes.problematic(value, value or '(none)', refid=msgid) prbid = self.document.set_id(prb) msg.add_backref(prbid) if len(field[1]): field[1][0][:] = [prb] else: field[1] += nodes.paragraph('', '', prb) break if pep is None: raise DataError('Document does not contain an RFC-2822 "PEP" ' 'header.') if pep == 0: # Special processing for PEP 0. pending = nodes.pending(peps.PEPZero) self.document.insert(1, pending) self.document.note_pending(pending) if len(header) < 2 or header[1][0].astext().lower() != 'title': raise DataError('No title!') for field in header: name = field[0].astext().lower() body = field[1] if len(body) > 1: raise DataError('PEP header field body contains multiple ' 'elements:\n%s' % field.pformat(level=1)) elif len(body) == 1: if not isinstance(body[0], nodes.paragraph): raise DataError('PEP header field body may only contain ' 'a single paragraph:\n%s' % field.pformat(level=1)) elif name == 'last-modified': date = time.strftime( '%d-%b-%Y', time.localtime(os.stat(self.document['source'])[8])) if cvs_url: body += nodes.paragraph( '', '', nodes.reference('', date, refuri=cvs_url)) else: # empty continue para = body[0] if name in ('author', 'bdfl-delegate', 'sponsor'): for node in para: if isinstance(node, nodes.reference): node.replace_self(peps.mask_email(node)) elif name == 'discussions-to': for node in para: if isinstance(node, nodes.reference): node.replace_self(peps.mask_email(node, pep)) elif name in ('replaces', 'superseded-by', 'requires'): newbody = [] space = nodes.Text(' ') for refpep in re.split(r',?\s+', body.astext()): pepno = int(refpep) newbody.append(nodes.reference( refpep, refpep, refuri=(self.document.settings.pep_base_url + self.pep_url % pepno))) newbody.append(space) para[:] = newbody[:-1] # drop trailing space elif name == 'last-modified': utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions) if cvs_url: date = para.astext() para[:] = [nodes.reference('', date, refuri=cvs_url)] elif name == 'content-type': pep_type = para.astext() uri = self.document.settings.pep_base_url + self.pep_url % 12 para[:] = [nodes.reference('', pep_type, refuri=uri)] elif name == 'version' and len(body): utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions)
def visit_reference(node: nodes.reference) -> None: """Mask email addresses if present.""" node.replace_self(peps.mask_email(node))
def apply(self): if not len(self.document): # @@@ replace these DataErrors with proper system messages raise DataError('Document tree is empty.') header = self.document[0] if not isinstance(header, nodes.field_list) or \ 'rfc2822' not in header['classes']: raise DataError('Document does not begin with an RFC-2822 ' 'header; it is not a PEP.') pep = None for field in header: if field[0].astext().lower() == 'pep': # should be the first field value = field[1].astext() try: pep = int(value) cvs_url = self.pep_cvs_url % pep except ValueError: pep = value cvs_url = None msg = self.document.reporter.warning( '"PEP" header must contain an integer; "%s" is an ' 'invalid value.' % pep, base_node=field) msgid = self.document.set_id(msg) prb = nodes.problematic(value, value or '(none)', refid=msgid) prbid = self.document.set_id(prb) msg.add_backref(prbid) if len(field[1]): field[1][0][:] = [prb] else: field[1] += nodes.paragraph('', '', prb) break if pep is None: raise DataError('Document does not contain an RFC-2822 "PEP" ' 'header.') if pep == 0: # Special processing for PEP 0. pending = nodes.pending(peps.PEPZero) self.document.insert(1, pending) self.document.note_pending(pending) if len(header) < 2 or header[1][0].astext().lower() != 'title': raise DataError('No title!') for field in header: name = field[0].astext().lower() body = field[1] if len(body) > 1: raise DataError('PEP header field body contains multiple ' 'elements:\n%s' % field.pformat(level=1)) elif len(body) == 1: if not isinstance(body[0], nodes.paragraph): raise DataError('PEP header field body may only contain ' 'a single paragraph:\n%s' % field.pformat(level=1)) elif name == 'last-modified': date = time.strftime( '%d-%b-%Y', time.localtime(os.stat(self.document['source'])[8])) if cvs_url: body += nodes.paragraph( '', '', nodes.reference('', date, refuri=cvs_url)) else: # empty continue para = body[0] if name in ('author', 'bdfl-delegate', 'pep-delegate', 'sponsor'): for node in para: if isinstance(node, nodes.reference): node.replace_self(peps.mask_email(node)) elif name == 'discussions-to': for node in para: if isinstance(node, nodes.reference): node.replace_self(peps.mask_email(node, pep)) elif name in ('replaces', 'superseded-by', 'requires'): newbody = [] space = nodes.Text(' ') for refpep in re.split(r',?\s+', body.astext()): pepno = int(refpep) newbody.append(nodes.reference( refpep, refpep, refuri=(self.document.settings.pep_base_url + self.pep_url % pepno))) newbody.append(space) para[:] = newbody[:-1] # drop trailing space elif name == 'last-modified': utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions) if cvs_url: date = para.astext() para[:] = [nodes.reference('', date, refuri=cvs_url)] elif name == 'content-type': pep_type = para.astext() uri = self.document.settings.pep_base_url + self.pep_url % 12 para[:] = [nodes.reference('', pep_type, refuri=uri)] elif name == 'version' and len(body): utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions)
def apply(self) -> None: if not Path(self.document["source"]).match("pep-*"): return # not a PEP file, exit early if not len(self.document): raise PEPParsingError("Document tree is empty.") header = self.document[0] if not isinstance( header, nodes.field_list) or "rfc2822" not in header["classes"]: raise PEPParsingError( "Document does not begin with an RFC-2822 header; it is not a PEP." ) # PEP number should be the first field pep_field = header[0] if pep_field[0].astext().lower() != "pep": raise PEPParsingError( "Document does not contain an RFC-2822 'PEP' header!") # Extract PEP number value = pep_field[1].astext() try: pep = int(value) except ValueError: raise PEPParsingError( f"'PEP' header must contain an integer. '{value}' is invalid!") # Special processing for PEP 0. if pep == 0: pending = nodes.pending(pep_zero.PEPZero) self.document.insert(1, pending) self.document.note_pending(pending) # If there are less than two headers in the preamble, or if Title is absent if len(header) < 2 or header[1][0].astext().lower() != "title": raise PEPParsingError("No title!") fields_to_remove = [] for field in header: name = field[0].astext().lower() body = field[1] if len(body) == 0: # body is empty continue elif len(body) > 1: msg = f"PEP header field body contains multiple elements:\n{field.pformat(level=1)}" raise PEPParsingError(msg) elif not isinstance(body[0], nodes.paragraph): # len(body) == 1 msg = f"PEP header field body may only contain a single paragraph:\n{field.pformat(level=1)}" raise PEPParsingError(msg) para = body[0] if name in {"author", "bdfl-delegate", "pep-delegate", "sponsor"}: # mask emails for node in para: if isinstance(node, nodes.reference): pep_num = pep if name == "discussions-to" else -1 node.replace_self(peps.mask_email(node, pep_num)) elif name in {"replaces", "superseded-by", "requires"}: # replace PEP numbers with normalised list of links to PEPs new_body = [] space = nodes.Text(" ") for ref_pep in re.split(r",?\s+", body.astext()): new_body.append( nodes.reference( ref_pep, ref_pep, refuri=(self.document.settings.pep_base_url + pep_url.format(int(ref_pep))))) new_body.append(space) para[:] = new_body[:-1] # drop trailing space elif name in {"last-modified", "content-type", "version"}: # Mark unneeded fields fields_to_remove.append(field) # Remove unneeded fields for field in fields_to_remove: field.parent.remove(field)