Checking for .nojekyll file
[rnaseq-cwl-training.git] / bin / util.py
1 import sys
2 import os
3 import json
4 from subprocess import Popen, PIPE
5
6 # Import this way to produce a more useful error message.
7 try:
8     import yaml
9 except ImportError:
10     print('Unable to import YAML module: please install PyYAML', file=sys.stderr)
11     sys.exit(1)
12
13
14 UNWANTED_FILES = [
15     '.nojekyll'
16 ]
17
18
19 class Reporter(object):
20     """Collect and report errors."""
21
22     def __init__(self):
23         """Constructor."""
24
25         super(Reporter, self).__init__()
26         self.messages = []
27
28
29     def check_field(self, filename, name, values, key, expected):
30         """Check that a dictionary has an expected value."""
31
32         if key not in values:
33             self.add(filename, '{0} does not contain {1}', name, key)
34         elif values[key] != expected:
35             self.add(filename, '{0} {1} is {2} not {3}', name, key, values[key], expected)
36
37
38     def check(self, condition, location, fmt, *args):
39         """Append error if condition not met."""
40
41         if not condition:
42             self.add(location, fmt, *args)
43
44
45     def add(self, location, fmt, *args):
46         """Append error unilaterally."""
47
48         if isinstance(location, type(None)):
49             coords = ''
50         elif isinstance(location, str):
51             coords = '{0}: '.format(location)
52         elif isinstance(location, tuple):
53             filename, line_number = location
54             coords = '{0}:{1}: '.format(*location)
55         else:
56             assert False, 'Unknown location "{0}"/{1}'.format(location, type(location))
57
58         self.messages.append(coords + fmt.format(*args))
59
60
61     def report(self, stream=sys.stdout):
62         """Report all messages."""
63
64         if not self.messages:
65             return
66         for m in sorted(self.messages):
67             print(m, file=stream)
68
69
70 def read_markdown(parser, path):
71     """
72     Get YAML and AST for Markdown file, returning
73     {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}.
74     """
75
76     # Split and extract YAML (if present).
77     with open(path, 'r') as reader:
78         body = reader.read()
79     metadata_raw, metadata_yaml, body = split_metadata(path, body)
80
81     # Split into lines.
82     metadata_len = 0 if metadata_raw is None else metadata_raw.count('\n')
83     lines = [(metadata_len+i+1, line, len(line)) for (i, line) in enumerate(body.split('\n'))]
84
85     # Parse Markdown.
86     cmd = 'ruby {0}'.format(parser)
87     p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
88     stdout_data, stderr_data = p.communicate(body)
89     doc = json.loads(stdout_data)
90
91     return {
92         'metadata': metadata_yaml,
93         'metadata_len': metadata_len,
94         'text': body,
95         'lines': lines,
96         'doc': doc
97     }
98
99
100 def split_metadata(path, text):
101     """
102     Get raw (text) metadata, metadata as YAML, and rest of body.
103     If no metadata, return (None, None, body).
104     """
105
106     metadata_raw = None
107     metadata_yaml = None
108     metadata_len = None
109
110     pieces = text.split('---', 2)
111     if len(pieces) == 3:
112         metadata_raw = pieces[1]
113         text = pieces[2]
114         try:
115             metadata_yaml = yaml.load(metadata_raw)
116         except yaml.YAMLError as e:
117             print('Unable to parse YAML header in {0}:\n{1}'.format(path, e), file=sys.stderr)
118             sys.exit(1)
119
120     return metadata_raw, metadata_yaml, text
121
122
123 def load_yaml(filename):
124     """
125     Wrapper around YAML loading so that 'import yaml' is only needed
126     in one file.
127     """
128
129     try:
130         with open(filename, 'r') as reader:
131             return yaml.load(reader)
132     except (yaml.YAMLError, FileNotFoundError) as e:
133         print('Unable to load YAML file {0}:\n{1}'.format(filename, e), file=sys.stderr)
134         sys.exit(1)
135
136
137 def check_unwanted_files(dir_path, reporter):
138     """
139     Check that unwanted files are not present.
140     """
141
142     for filename in UNWANTED_FILES:
143         path = os.path.join(dir_path, filename)
144         reporter.check(not os.path.exists(path),
145                        path,
146                        "Unwanted file found")