/usr/share/pyshared/twisted/lore/lint.py is in python-twisted-lore 11.1.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | # Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Checker for common errors in Lore documents.
"""
from xml.dom import minidom as dom
import parser, urlparse, os.path
from twisted.lore import tree, process
from twisted.web import domhelpers
from twisted.python import reflect
# parser.suite in Python 2.3 raises SyntaxError, <2.3 raises parser.ParserError
parserErrors = (SyntaxError, parser.ParserError)
class TagChecker:
def check(self, dom, filename):
self.hadErrors = 0
for method in reflect.prefixedMethods(self, 'check_'):
method(dom, filename)
if self.hadErrors:
raise process.ProcessingFailure("invalid format")
def _reportError(self, filename, element, error):
hlint = element.hasAttribute('hlint') and element.getAttribute('hlint')
if hlint != 'off':
self.hadErrors = 1
pos = getattr(element, '_markpos', None) or (0, 0)
print "%s:%s:%s: %s" % ((filename,)+pos+(error,))
class DefaultTagChecker(TagChecker):
def __init__(self, allowedTags, allowedClasses):
self.allowedTags = allowedTags
self.allowedClasses = allowedClasses
def check_disallowedElements(self, dom, filename):
def m(node, self=self):
return not self.allowedTags(node.tagName)
for element in domhelpers.findElements(dom, m):
self._reportError(filename, element,
'unrecommended tag %s' % element.tagName)
def check_disallowedClasses(self, dom, filename):
def matcher(element, self=self):
if not element.hasAttribute('class'):
return 0
checker = self.allowedClasses.get(element.tagName, lambda x:0)
return not checker(element.getAttribute('class'))
for element in domhelpers.findElements(dom, matcher):
self._reportError(filename, element,
'unknown class %s' %element.getAttribute('class'))
def check_quote(self, doc, filename):
def matcher(node):
return ('"' in getattr(node, 'data', '') and
not isinstance(node, dom.Comment) and
not [1 for n in domhelpers.getParents(node)[1:-1]
if n.tagName in ('pre', 'code')])
for node in domhelpers.findNodes(doc, matcher):
self._reportError(filename, node.parentNode, 'contains quote')
def check_styleattr(self, dom, filename):
for node in domhelpers.findElementsWithAttribute(dom, 'style'):
self._reportError(filename, node, 'explicit style')
def check_align(self, dom, filename):
for node in domhelpers.findElementsWithAttribute(dom, 'align'):
self._reportError(filename, node, 'explicit alignment')
def check_style(self, dom, filename):
for node in domhelpers.findNodesNamed(dom, 'style'):
if domhelpers.getNodeText(node) != '':
self._reportError(filename, node, 'hand hacked style')
def check_title(self, dom, filename):
doc = dom.documentElement
title = domhelpers.findNodesNamed(dom, 'title')
if len(title)!=1:
return self._reportError(filename, doc, 'not exactly one title')
h1 = domhelpers.findNodesNamed(dom, 'h1')
if len(h1)!=1:
return self._reportError(filename, doc, 'not exactly one h1')
if domhelpers.getNodeText(h1[0]) != domhelpers.getNodeText(title[0]):
self._reportError(filename, h1[0], 'title and h1 text differ')
def check_80_columns(self, dom, filename):
for node in domhelpers.findNodesNamed(dom, 'pre'):
# the ps/pdf output is in a font that cuts off at 80 characters,
# so this is enforced to make sure the interesting parts (which
# are likely to be on the right-hand edge) stay on the printed
# page.
for line in domhelpers.gatherTextNodes(node, 1).split('\n'):
if len(line.rstrip()) > 80:
self._reportError(filename, node,
'text wider than 80 columns in pre')
for node in domhelpers.findNodesNamed(dom, 'a'):
if node.getAttribute('class').endswith('listing'):
try:
fn = os.path.dirname(filename)
fn = os.path.join(fn, node.getAttribute('href'))
lines = open(fn,'r').readlines()
except:
self._reportError(filename, node,
'bad listing href: %r' %
node.getAttribute('href'))
continue
for line in lines:
if len(line.rstrip()) > 80:
self._reportError(filename, node,
'listing wider than 80 columns')
def check_pre_py_listing(self, dom, filename):
for node in domhelpers.findNodesNamed(dom, 'pre'):
if node.getAttribute('class') == 'python':
try:
text = domhelpers.getNodeText(node)
# Fix < and >
text = text.replace('>', '>').replace('<', '<')
# Strip blank lines
lines = filter(None,[l.rstrip() for l in text.split('\n')])
# Strip leading space
while not [1 for line in lines if line[:1] not in ('',' ')]:
lines = [line[1:] for line in lines]
text = '\n'.join(lines) + '\n'
try:
parser.suite(text)
except parserErrors, e:
# Pretend the "..." idiom is syntactically valid
text = text.replace("...","'...'")
parser.suite(text)
except parserErrors, e:
self._reportError(filename, node,
'invalid python code:' + str(e))
def check_anchor_in_heading(self, dom, filename):
headingNames = ['h%d' % n for n in range(1,7)]
for hname in headingNames:
for node in domhelpers.findNodesNamed(dom, hname):
if domhelpers.findNodesNamed(node, 'a'):
self._reportError(filename, node, 'anchor in heading')
def check_texturl_matches_href(self, dom, filename):
for node in domhelpers.findNodesNamed(dom, 'a'):
if not node.hasAttribute('href'):
continue
text = domhelpers.getNodeText(node)
proto = urlparse.urlparse(text)[0]
if proto and ' ' not in text:
if text != node.getAttribute('href'):
self._reportError(filename, node,
'link text does not match href')
def check_lists(self, dom, filename):
for node in (domhelpers.findNodesNamed(dom, 'ul')+
domhelpers.findNodesNamed(dom, 'ol')):
if not node.childNodes:
self._reportError(filename, node, 'empty list')
for child in node.childNodes:
if child.nodeName != 'li':
self._reportError(filename, node,
'only list items allowed in lists')
def list2dict(l):
d = {}
for el in l:
d[el] = None
return d
classes = list2dict(['shell', 'API', 'python', 'py-prototype', 'py-filename',
'py-src-string', 'py-signature', 'py-src-parameter',
'py-src-identifier', 'py-src-keyword'])
tags = list2dict(["html", "title", "head", "body", "h1", "h2", "h3", "ol", "ul",
"dl", "li", "dt", "dd", "p", "code", "img", "blockquote", "a",
"cite", "div", "span", "strong", "em", "pre", "q", "table",
"tr", "td", "th", "style", "sub", "sup", "link"])
span = list2dict(['footnote', 'manhole-output', 'index'])
div = list2dict(['note', 'boxed', 'doit'])
a = list2dict(['listing', 'py-listing', 'html-listing', 'absolute'])
pre = list2dict(['python', 'shell', 'python-interpreter', 'elisp'])
allowed = {'code': classes.has_key, 'span': span.has_key, 'div': div.has_key,
'a': a.has_key, 'pre': pre.has_key, 'ul': lambda x: x=='toc',
'ol': lambda x: x=='toc', 'li': lambda x: x=='ignoretoc'}
def getDefaultChecker():
return DefaultTagChecker(tags.has_key, allowed)
def doFile(file, checker):
doc = tree.parseFileAndReport(file)
if doc:
checker.check(doc, file)
|