diff --git a/bin/lesson_check.py b/bin/lesson_check.py index 613425f1b08af455fd6af1a98389eac53836bead..b2f0308133396ddfbe5dedc2ba382d8f1ec4a9b4 100755 --- a/bin/lesson_check.py +++ b/bin/lesson_check.py @@ -98,6 +98,10 @@ def parse_args(): """Parse command-line arguments.""" parser = OptionParser() + parser.add_option('-l', '--linelen', + default=False, + dest='line_len', + help='Check line lengths') parser.add_option('-p', '--parser', default=None, dest='parser', @@ -127,7 +131,9 @@ def check_config(args): def read_all_markdown(args, source_dir): - """Read source files, returning {path : {'metadta':yaml, 'text':text, 'doc':doc}}.""" + """Read source files, returning + {path : {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}} + """ all_dirs = [os.path.join(source_dir, d) for d in SOURCE_DIRS] all_patterns = [os.path.join(d, '*.md') for d in all_dirs] @@ -160,17 +166,22 @@ def check_fileset(source_dir, reporter, filenames_present): else: reporter.add(None, 'Episode {0} has badly-formatted filename', filename) - # Check episode filename numbering. + # Check for duplicate episode numbers. reporter.check(len(seen) == len(set(seen)), None, 'Duplicate episode numbers {0} vs {1}', sorted(seen), sorted(set(seen))) + + # Check that numbers are consecutive. seen = [int(s) for s in seen] seen.sort() - reporter.check(all([i+1 == n for (i, n) in enumerate(seen)]), - None, - 'Missing or non-consecutive episode numbers {0}', - seen) + clean = True + for i in range(len(seen) - 1): + clean = clean and ((seen[i+1] - seen[i]) == 1) + reporter.check(clean, + None, + 'Missing or non-consecutive episode numbers {0}', + seen) def create_checker(args, filename, info): @@ -192,7 +203,7 @@ def require(condition, message): class CheckBase(object): """Base class for checking Markdown files.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): """Cache arguments for checking.""" super(CheckBase, self).__init__() @@ -202,6 +213,7 @@ class CheckBase(object): self.metadata = metadata self.metadata_len = metadata_len self.text = text + self.lines = lines self.doc = doc self.layout = None @@ -230,15 +242,12 @@ class CheckBase(object): def check_text(self): """Check the raw text of the lesson body.""" - offset = 0 - if self.metadata_len is not None: - offset = self.metadata_len - lines = [(offset+i+1, l, len(l)) for (i, l) in enumerate(self.text.split('\n'))] - over = [i for (i, l, n) in lines if (n > MAX_LINE_LEN) and (not l.startswith('!'))] - self.reporter.check(not over, - self.filename, - 'Line(s) are too long: {0}', - ', '.join([str(i) for i in over])) + if self.args.line_len: + over = [i for (i, l, n) in self.lines if (n > MAX_LINE_LEN) and (not l.startswith('!'))] + self.reporter.check(not over, + self.filename, + 'Line(s) are too long: {0}', + ', '.join([str(i) for i in over])) def check_blockquote_classes(self): @@ -315,8 +324,8 @@ class CheckBase(object): class CheckNonJekyll(CheckBase): """Check a file that isn't translated by Jekyll.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): - super(CheckNonJekyll, self).__init__(args, filename, metadata, metadata_len, text, doc) + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): + super(CheckNonJekyll, self).__init__(args, filename, metadata, metadata_len, text, lines, doc) def check_metadata(self): @@ -328,16 +337,16 @@ class CheckNonJekyll(CheckBase): class CheckIndex(CheckBase): """Check the main index page.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): - super(CheckIndex, self).__init__(args, filename, metadata, metadata_len, text, doc) + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): + super(CheckIndex, self).__init__(args, filename, metadata, metadata_len, text, lines, doc) self.layout = 'lesson' class CheckEpisode(CheckBase): """Check an episode page.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): - super(CheckEpisode, self).__init__(args, filename, metadata, metadata_len, text, doc) + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): + super(CheckEpisode, self).__init__(args, filename, metadata, metadata_len, text, lines, doc) def check_metadata(self): super(CheckEpisode, self).check_metadata() @@ -352,16 +361,16 @@ class CheckEpisode(CheckBase): class CheckReference(CheckBase): """Check the reference page.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): - super(CheckReference, self).__init__(args, filename, metadata, metadata_len, text, doc) + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): + super(CheckReference, self).__init__(args, filename, metadata, metadata_len, text, lines, doc) self.layout = 'reference' class CheckGeneric(CheckBase): """Check a generic page.""" - def __init__(self, args, filename, metadata, metadata_len, text, doc): - super(CheckGeneric, self).__init__(args, filename, metadata, metadata_len, text, doc) + def __init__(self, args, filename, metadata, metadata_len, text, lines, doc): + super(CheckGeneric, self).__init__(args, filename, metadata, metadata_len, text, lines, doc) self.layout = 'page' diff --git a/bin/util.py b/bin/util.py index 23245be8a7858d04a0b8e3d89ba1748224439573..7d18e6c8c599b88e294eb87145545602cef8b031 100644 --- a/bin/util.py +++ b/bin/util.py @@ -56,7 +56,10 @@ class Reporter(object): def read_markdown(parser, path): - """Get YAML and AST for Markdown file, returning {'metadata':yaml, 'text': text, 'doc':doc}.""" + """ + Get YAML and AST for Markdown file, returning + {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}. + """ # Split and extract YAML (if present). metadata = None @@ -73,6 +76,10 @@ def read_markdown(parser, path): metadata_len = pieces[1].count('\n') body = pieces[2] + # Split into lines. + offset = 0 if metadata_len is None else metadata_len + lines = [(offset+i+1, l, len(l)) for (i, l) in enumerate(body.split('\n'))] + # Parse Markdown. cmd = 'ruby {0}'.format(parser) p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True) @@ -83,5 +90,6 @@ def read_markdown(parser, path): 'metadata': metadata, 'metadata_len': metadata_len, 'text': body, + 'lines': lines, 'doc': doc }