3 from subprocess import Popen, PIPE
8 print('Unable to import YAML module: please install PyYAML', file=sys.stderr)
11 class Reporter(object):
12 """Collect and report errors."""
17 super(Reporter, self).__init__()
21 def check_field(self, filename, name, values, key, expected):
22 """Check that a dictionary has an expected value."""
25 self.add(filename, '{0} does not contain {1}', name, key)
26 elif values[key] != expected:
27 self.add(filename, '{0} {1} is {2} not {3}', name, key, values[key], expected)
30 def check(self, condition, location, fmt, *args):
31 """Append error if condition not met."""
34 self.add(location, fmt, *args)
37 def add(self, location, fmt, *args):
38 """Append error unilaterally."""
40 if isinstance(location, type(None)):
42 elif isinstance(location, str):
43 coords = '{0}: '.format(location)
44 elif isinstance(location, tuple):
45 filename, line_number = location
46 coords = '{0}:{1}: '.format(*location)
48 assert False, 'Unknown location "{0}"/{1}'.format(location, type(location))
50 self.messages.append(coords + fmt.format(*args))
53 def report(self, stream=sys.stdout):
54 """Report all messages."""
58 for m in sorted(self.messages):
62 def read_markdown(parser, path):
64 Get YAML and AST for Markdown file, returning
65 {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}.
68 # Split and extract YAML (if present).
69 with open(path, 'r') as reader:
71 metadata_raw, metadata_yaml, body = split_metadata(path, body)
74 metadata_len = 0 if metadata_raw is None else metadata_raw.count('\n')
75 lines = [(metadata_len+i+1, line, len(line)) for (i, line) in enumerate(body.split('\n'))]
78 cmd = 'ruby {0}'.format(parser)
79 p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
80 stdout_data, stderr_data = p.communicate(body)
81 doc = json.loads(stdout_data)
84 'metadata': metadata_yaml,
85 'metadata_len': metadata_len,
92 def split_metadata(path, text):
94 Get raw (text) metadata, metadata as YAML, and rest of body.
95 If no metadata, return (None, None, body).
102 pieces = text.split('---', 2)
104 metadata_raw = pieces[1]
107 metadata_yaml = yaml.load(metadata_raw)
108 except yaml.YAMLError as e:
109 print('Unable to parse YAML header in {0}:\n{1}'.format(path, e), file=sys.stderr)
112 return metadata_raw, metadata_yaml, text
115 def load_yaml(filename):
117 Wrapper around YAML loading so that 'import yaml' and error
118 handling is only needed in one place.
121 with open(filename, 'r') as reader:
122 return yaml.load(reader)