proto_checker.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Checks protobuf files for illegal imports."""
  5. import codecs
  6. import os
  7. import re
  8. import results
  9. from rules import Rule, MessageRule
  10. class ProtoChecker(object):
  11. EXTENSIONS = [
  12. '.proto',
  13. ]
  14. # The maximum number of non-import lines we can see before giving up.
  15. _MAX_UNINTERESTING_LINES = 50
  16. # The maximum line length, this is to be efficient in the case of very long
  17. # lines (which can't be import).
  18. _MAX_LINE_LENGTH = 128
  19. # This regular expression will be used to extract filenames from import
  20. # statements.
  21. _EXTRACT_IMPORT_PATH = re.compile(
  22. r'[ \t]*[ \t]*import[ \t]+"(.*)"')
  23. def __init__(self, verbose, resolve_dotdot=False, root_dir=''):
  24. self._verbose = verbose
  25. self._resolve_dotdot = resolve_dotdot
  26. self._root_dir = root_dir
  27. def IsFullPath(self, import_path):
  28. """Checks if the given path is a valid path starting from |_root_dir|."""
  29. match = re.match(r'(.*)/([^/]*\.proto)', import_path)
  30. if not match:
  31. return False
  32. return os.path.isdir(self._root_dir + "/" + match.group(1))
  33. def CheckLine(self, rules, line, dependee_path, fail_on_temp_allow=False):
  34. """Checks the given line with the given rule set.
  35. Returns a tuple (is_import, dependency_violation) where
  36. is_import is True only if the line is an import
  37. statement, and dependency_violation is an instance of
  38. results.DependencyViolation if the line violates a rule, or None
  39. if it does not.
  40. """
  41. found_item = self._EXTRACT_IMPORT_PATH.match(line)
  42. if not found_item:
  43. return False, None # Not a match
  44. import_path = found_item.group(1)
  45. if '\\' in import_path:
  46. return True, results.DependencyViolation(
  47. import_path,
  48. MessageRule('Import paths may not include backslashes.'),
  49. rules)
  50. if '/' not in import_path:
  51. # Don't fail when no directory is specified. We may want to be more
  52. # strict about this in the future.
  53. if self._verbose:
  54. print(' WARNING: import specified with no directory: ' + import_path)
  55. return True, None
  56. if self._resolve_dotdot and '../' in import_path:
  57. dependee_dir = os.path.dirname(dependee_path)
  58. import_path = os.path.join(dependee_dir, import_path)
  59. import_path = os.path.relpath(import_path, self._root_dir)
  60. if not self.IsFullPath(import_path):
  61. return True, None
  62. rule = rules.RuleApplyingTo(import_path, dependee_path)
  63. if (rule.allow == Rule.DISALLOW or
  64. (fail_on_temp_allow and rule.allow == Rule.TEMP_ALLOW)):
  65. return True, results.DependencyViolation(import_path, rule, rules)
  66. return True, None
  67. def CheckFile(self, rules, filepath):
  68. if self._verbose:
  69. print('Checking: ' + filepath)
  70. dependee_status = results.DependeeStatus(filepath)
  71. last_import = 0
  72. with codecs.open(filepath, encoding='utf-8') as f:
  73. for line_num, line in enumerate(f):
  74. if line_num - last_import > self._MAX_UNINTERESTING_LINES:
  75. break
  76. line = line.strip()
  77. is_import, violation = self.CheckLine(rules, line, filepath)
  78. if is_import:
  79. last_import = line_num
  80. if violation:
  81. dependee_status.AddViolation(violation)
  82. return dependee_status
  83. @staticmethod
  84. def IsProtoFile(file_path):
  85. """Returns True iff the given path ends in one of the extensions
  86. handled by this checker.
  87. """
  88. return os.path.splitext(file_path)[1] in ProtoChecker.EXTENSIONS
  89. def ShouldCheck(self, file_path):
  90. """Check if the new #include file path should be presubmit checked.
  91. Args:
  92. file_path: file path to be checked
  93. Return:
  94. bool: True if the file should be checked; False otherwise.
  95. """
  96. return self.IsProtoFile(file_path)