4 from subprocess import Popen, PIPE
7 class Reporter(object):
8 """Collect and report errors."""
10 def __init__(self, args):
13 super(Reporter, self).__init__()
17 def check_field(self, filename, name, values, key, expected):
18 """Check that a dictionary has an expected value."""
21 self.add(filename, '{0} does not contain {1}', name, key)
22 elif values[key] != expected:
23 self.add(filename, '{0} {1} is {2} not {3}', name, key, values[key], expected)
26 def check(self, condition, location, fmt, *args):
27 """Append error if condition not met."""
30 self.add(location, fmt, *args)
33 def add(self, location, fmt, *args):
34 """Append error unilaterally."""
36 if isinstance(location, type(None)):
38 elif isinstance(location, str):
39 coords = '{0}: '.format(location)
40 elif isinstance(location, tuple):
41 filename, line_number = location
42 coords = '{0}:{1}: '.format(*location)
44 assert False, 'Unknown location "{0}"/{1}'.format(location, type(location))
46 self.messages.append(coords + fmt.format(*args))
49 def report(self, stream=sys.stdout):
50 """Report all messages."""
54 for m in self.messages:
58 def read_markdown(parser, path):
60 Get YAML and AST for Markdown file, returning
61 {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}.
64 # Split and extract YAML (if present).
67 with open(path, 'r') as reader:
69 pieces = body.split('---', 2)
72 metadata = yaml.load(pieces[1])
73 except yaml.YAMLError as e:
74 print('Unable to parse YAML header in {0}:\n{1}'.format(path, e))
76 metadata_len = pieces[1].count('\n')
80 offset = 0 if metadata_len is None else metadata_len
81 lines = [(offset+i+1, l, len(l)) for (i, l) in enumerate(body.split('\n'))]
84 cmd = 'ruby {0}'.format(parser)
85 p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
86 stdout_data, stderr_data = p.communicate(body)
87 doc = json.loads(stdout_data)
91 'metadata_len': metadata_len,