You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
5.5 KiB

  1. #!/usr/bin/env python3
  2. import re
  3. import sys
  4. anchor = '###'
  5. min_entries_per_section = 3
  6. auth_keys = ['apiKey', 'OAuth', 'X-Mashape-Key', 'No']
  7. punctuation = ['.', '?', '!']
  8. https_keys = ['Yes', 'No']
  9. cors_keys = ['Yes', 'No', 'Unknown']
  10. index_title = 0
  11. index_desc = 1
  12. index_auth = 2
  13. index_https = 3
  14. index_cors = 4
  15. index_link = 5
  16. errors = []
  17. title_links = []
  18. anchor_re = re.compile('###\s(.+)')
  19. section_title_re = re.compile('\*\s\[(.*)\]')
  20. def add_error(line_num, message):
  21. """adds an error to the dynamic error list"""
  22. err = '(L{:03d}) {}'.format(line_num + 1, message)
  23. errors.append(err)
  24. def check_alphabetical(lines):
  25. """
  26. checks if all entries per section are in alphabetical order based in entry title
  27. """
  28. sections = {}
  29. section_line_num = {}
  30. for line_num, line in enumerate(lines):
  31. if line.startswith(anchor):
  32. category = line.split(anchor)[1].strip()
  33. sections[category] = []
  34. section_line_num[category] = line_num
  35. continue
  36. if not line.startswith('|') or line.startswith('|---'):
  37. continue
  38. title = [x.strip() for x in line.split('|')[1:-1]][0].upper()
  39. sections[category].append(title)
  40. for category, entries in sections.items():
  41. if sorted(entries) != entries:
  42. add_error(section_line_num[category], "{} section is not in alphabetical order".format(category))
  43. def check_entry(line_num, segments):
  44. # START Title
  45. title = segments[index_title].upper()
  46. if title.endswith(' API'):
  47. add_error(line_num, 'Title should not contain "API"')
  48. # END Title
  49. # START Description
  50. # first character should be capitalized
  51. char = segments[index_desc][0]
  52. if char.upper() != char:
  53. add_error(line_num, "first character of description is not capitalized")
  54. # last character should not punctuation
  55. char = segments[index_desc][-1]
  56. if char in punctuation:
  57. add_error(line_num, "description should not end with {}".format(char))
  58. desc_length = len(segments[index_desc])
  59. if desc_length > 100:
  60. add_error(line_num, "description should not exceed 100 characters (currently {})".format(desc_length))
  61. # END Description
  62. # START Auth
  63. # values should conform to valid options only
  64. auth = segments[index_auth]
  65. if auth != 'No' and (not auth.startswith('`') or not auth.endswith('`')):
  66. add_error(line_num, "auth value is not enclosed with `backticks`")
  67. if auth.replace('`', '') not in auth_keys:
  68. add_error(line_num, "{} is not a valid Auth option".format(auth))
  69. # END Auth
  70. # START HTTPS
  71. # values should conform to valid options only
  72. https = segments[index_https]
  73. if https not in https_keys:
  74. add_error(line_num, "{} is not a valid HTTPS option".format(https))
  75. # END HTTPS
  76. # START CORS
  77. # values should conform to valid options only
  78. cors = segments[index_cors]
  79. if cors not in cors_keys:
  80. add_error(line_num, "{} is not a valid CORS option".format(cors))
  81. # END CORS
  82. # START Link
  83. # url should be wrapped in '[Go!]()' Markdown syntax
  84. link = segments[index_link]
  85. if not link.startswith('[Go!](http') or not link.endswith(')'):
  86. add_error(line_num, 'link syntax should be "[Go!](LINK)"')
  87. # END Link
  88. def check_format(filename):
  89. """
  90. validates that each line is formatted correctly,
  91. appending to error list as needed
  92. """
  93. with open(filename) as fp:
  94. lines = list(line.rstrip() for line in fp)
  95. check_alphabetical(lines)
  96. # START Check Entries
  97. num_in_category = min_entries_per_section + 1
  98. category = ""
  99. category_line = 0
  100. for line_num, line in enumerate(lines):
  101. if section_title_re.match(line):
  102. title_links.append(section_title_re.match(line).group(1))
  103. # check each section for the minimum number of entries
  104. if line.startswith(anchor):
  105. match = anchor_re.match(line)
  106. if match:
  107. if match.group(1) not in title_links:
  108. add_error(line_num, "section header ({}) not added as a title link".format(match.group(1)))
  109. else:
  110. add_error(line_num, "section header is not formatted correctly")
  111. if num_in_category < min_entries_per_section:
  112. add_error(category_line, "{} section does not have the minimum {} entries (only has {})".format(
  113. category, min_entries_per_section, num_in_category))
  114. category = line.split(' ')[1]
  115. category_line = line_num
  116. num_in_category = 0
  117. continue
  118. if not line.startswith('|') or line.startswith('|---'):
  119. continue
  120. num_in_category += 1
  121. segments = line.split('|')[1:-1]
  122. # START Global
  123. for segment in segments:
  124. # every line segment should start and end with exactly 1 space
  125. if len(segment) - len(segment.lstrip()) != 1 or len(segment) - len(segment.rstrip()) != 1:
  126. add_error(line_num, "each segment must start and end with exactly 1 space")
  127. # END Global
  128. segments = [seg.strip() for seg in segments]
  129. check_entry(line_num, segments)
  130. # END Check Entries
  131. def main():
  132. num_args = len(sys.argv)
  133. if num_args < 2:
  134. print("No file passed (file should contain Markdown table syntax)")
  135. sys.exit(1)
  136. check_format(sys.argv[1])
  137. if len(errors) > 0:
  138. for err in errors:
  139. print(err)
  140. sys.exit(1)
  141. if __name__ == "__main__":
  142. main()