123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- from typing import List
- import os
- import sys
- import argparse
- from enum import Flag
- import requests
- from CommonMark import Parser, dumpAST
- def url_valid(url: str) -> bool:
- try:
- request = requests.head(url, timeout=1.0)
- if request.ok:
- return True
- else:
- return False
- except Exception:
- return False
- class ValidatorFlags(Flag):
-
- ALL = ~0
-
- IMAGE = (1 << 1)
-
- LINK = (1 << 2)
-
- ANCHOR = (1 << 3)
-
- EXTERNAL = (1 << 4)
-
- LOCAL = (1 << 5)
- class Validator:
- def __init__(self, filename: str) -> None:
- self._filename = filename
- self._anchors = []
- def set_flags(self, flags):
- self._flags = flags
- def validate(self) -> None:
- with open(self._filename) as fin:
- parser = Parser()
- document = parser.parse(fin.read())
- for node, starttag in document.walker():
- self.preprocess(node, starttag)
- for node, starttag in document.walker():
- self.check(node, starttag)
- def get_pos(self, node) -> str:
- cur = node.parent
- while cur is not None:
- if cur.sourcepos is not None:
- return "{}:{}".format(self._filename, cur.sourcepos[0][0])
- else:
- cur = cur.parent
- return "{}".format(self._filename)
- def validate_link_destination(self, node) -> None:
-
- if node.destination.startswith("http://") or node.destination.startswith("https://"):
- if self._flags & ValidatorFlags.EXTERNAL:
- if not url_valid(node.destination):
- print("{}: error: {} failed to load".format(self.get_pos(node),
- node.destination))
- else:
- print("{}: OK".format(node.destination))
- elif node.destination.startswith("#"):
- if self._flags & ValidatorFlags.ANCHOR:
- self.validate_anchor(node, node.destination[1:])
- else:
- page, *rest = node.destination.split("#", 1)
- filename = page + ".md"
- if not os.path.exists(filename):
- print("{}: error: {} does not exist".format(self.get_pos(node),
- filename),
- file=sys.stderr)
- if rest and self._flags & ValidatorFlags.ANCHOR:
-
-
-
- pass
- def validate_anchor(self, node, anchor: str) -> None:
- if anchor not in self._anchors:
- print("{}: error: {} anchor does not exist".format(self.get_pos(node),
- anchor),
- file=sys.stderr)
- def validate_image_destination(self, node) -> None:
-
- if node.destination.startswith("http://") or node.destination.startswith("https://"):
- if self._flags & ValidatorFlags.EXTERNAL:
- if not url_valid(node.destination):
- print("{}: error: {} failed to load".format(self.get_pos(node),
- node.destination))
- elif not os.path.exists(node.destination):
- print("{}: error: {} does not exist".format(self.get_pos(node),
- node.destination),
- file=sys.stderr)
- def get_text(self, node) -> str:
- result = ""
- for child, starttag in node.walker():
- if child.t == "text":
- result += child.literal
- return result
- def preprocess(self, node, starttag: bool):
- if starttag:
- if node.t == "heading":
- title = self.get_text(node)
- anchor = title.lower().replace(" ", "-")
- self._anchors.append(anchor)
- def check(self, node, starttag: bool):
- if starttag:
- if node.t == "link" and self._flags & ValidatorFlags.LINK:
-
-
-
-
- self.validate_link_destination(node)
- elif node.t == "image" and self._flags & ValidatorFlags.IMAGE:
-
-
-
-
- self.validate_image_destination(node)
- def parse_args(argv: List[str]) -> argparse.Namespace:
-
- parser = argparse.ArgumentParser(description="Flexlay - SuperTux Editor")
- parser.add_argument("FILE", action="store", type=str, nargs="+",
- help=".md file to verify")
- parser.add_argument("--validate", metavar="CATEGORY", type=str, default="all")
- parser.add_argument("--dump-ast", action='store_true', default=False)
- return parser.parse_args(argv[1:])
- def main(argv: List[str]):
- args = parse_args(argv)
- flags = ValidatorFlags(0)
- categories = args.validate.split(",")
- for category in categories:
- category = category.lower()
- if category == "all":
- flags |= ValidatorFlags.ALL
- elif category == "external":
- flags |= ValidatorFlags.EXTERNAL
- elif category == "local":
- flags |= ValidatorFlags.LOCAL
- elif category == "link":
- flags |= ValidatorFlags.LINK
- elif category == "anchor":
- flags |= ValidatorFlags.ANCHOR
- elif category == "image":
- flags |= ValidatorFlags.IMAGE
- else:
- raise Exception("unknown category '{}'".format(category))
- for filename in args.FILE:
- if args.dump_ast:
- with open(filename) as fin:
- parser = Parser()
- document = parser.parse(fin.read())
- dumpAST(document)
- else:
- validator = Validator(filename)
- validator.set_flags(flags)
- validator.validate()
- if __name__ == "__main__":
- main(sys.argv)
|