Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: reviewbot/handlers/policy_checklist/parser.py

Issue 23531026: Remove the review bot app. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2013 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
5 import re
6
7 import util
8
9
10 CONTEXT_THRESHOLD = 12
11 PROPERTY_NAME_RE = re.compile(r"^\s*'(\w+)'\s*:")
12
13
14 def nmin(*args):
15 """Calculates the minimum of |args|, ignoring None entries."""
16 values = [v for v in args if v is not None]
17 return None if len(values) == 0 else min(values)
18
19
20 def nmax(*args):
21 """Calculates the maximum of |args|, ignoring None entries."""
22 values = [v for v in args if v is not None]
23 return None if len(values) == 0 else max(values)
24
25
26 def nadd(a, b):
27 """Calculates a + b, returning None if either a or b is None"""
28 return None if (a is None or b is None) else a + b
29
30
31 def nsub(a, b):
32 """Calculates a - b, returning None if either a or b is None"""
33 return None if (a is None or b is None) else a - b
34
35
36 def get_indentation_level(line):
37 """Returns the indentation level (number of leading spaces) for |line|."""
38 nspaces = len(line) - len(line.lstrip(' '))
39 return None if nspaces == 0 else nspaces
40
41
42 class PolicyChangeParser(object):
43 """Parses a policy_templates.json diff to identify logical changes.
44
45 This takes a list of triples of the form (old_line, new_line, text) as
46 returned by patching.ParsePatchToLines and produces a list of dictionaries
47 describing the logical changes that have been made. The dictionaries contain
48 these keys:
49 * start: A pair (old_line, new_line) indicating where the change starts.
50 * end: A pair (old_line, new_line) indicating where the change ends.
51 * comment_pos: A pair (old_line, new_line), indicating a suitable place to
52 put an inline comment. This is typically the line where the
53 policy name is found in the diff.
54 * additions: Whether there have been line additions.
55 * removals: Whether there have been line removals.
56 """
57
58 def __init__(self, lines):
59 self.lines = lines
60 self.chunks_list = []
61 self.reset()
62
63 def run(self):
64 """Main parsing function.
65
66 The code goes over the diff line by line, keeping track of the current line.
67 It keeps track of the current line numbers, and where the last changes
68 happened in the old and new version of the file.
69
70 Certain events trigger start of a new logical change. These are
71 discontinuities in the cursor position and decreases of the indentation
72 level. Once a block closes, the information for that block is recorded in
73 the result list.
74 """
75 self.chunks_list = []
76 self.last_change = [None, None]
77 cursor = [None, None]
78 self.reset()
79 for (a_line, b_line, line) in self.lines:
80 # Skip comment lines.
81 if line.startswith('#'):
82 continue
83
84 # See whether the current line has a JSON property.
85 keyword = None
86 match = PROPERTY_NAME_RE.match(line)
87 if match:
88 keyword = match.group(1).lower()
89
90
91 # Check whether the current block closes.
92 line_indent = get_indentation_level(line)
93 if (self.block_indent is not None and
94 line_indent is not None and
95 line_indent < self.block_indent):
96 self.block_closed = True
97
98 # Update various cursors.
99 cursor = [nmax(a_line, cursor[0]), nmax(b_line, cursor[1])]
100 offset = nmin(nsub(cursor[0], self.last_change[0]),
101 nsub(cursor[1], self.last_change[1]))
102
103 # Update change tracking state.
104 if a_line is not None and b_line is None:
105 self.removals = True
106 self.last_change[0] = a_line
107 self.text_changed |= any([c.isalnum() for c in line])
108 elif a_line is None and b_line is not None:
109 self.additions = True
110 self.last_change[1] = b_line
111 self.text_changed |= any([c.isalnum() for c in line])
112
113 # If the indentation block closes or the last chunk is too far away,
114 # assume a new one starts.
115 if (self.block_closed or
116 (offset is not None and (offset > CONTEXT_THRESHOLD))):
117 self.flush_chunk()
118
119 # Try to figure out block indent from properties exclusively used for
120 # policy definitions.
121 if (self.block_indent is None and
122 keyword in ('id', 'schema', 'future', 'features', 'supported_on',
123 'example_value', 'deprecated')):
124 self.block_indent = line_indent
125
126 # Put the comment on the policy name property if we see it fly by.
127 if keyword == 'name':
128 # Filter out name labels on enum items and schemas.
129 if self.block_indent is not None and self.block_indent != line_indent:
130 pass
131 elif a_line is not None and b_line is None:
132 self.comment_pos[0] = a_line
133 elif a_line is None and b_line is not None:
134 self.comment_pos[1] = b_line
135
136 self.chunk_start = [nmin(self.last_change[0], self.chunk_start[0]),
137 nmin(self.last_change[1], self.chunk_start[1])]
138
139 # Flush the last chunk.
140 if self.chunk_start != [None, None]:
141 self.flush_chunk()
142
143 def flush_chunk(self):
144 if self.text_changed:
145 comment_pos = [nmax(self.chunk_start[0], self.comment_pos[0]),
146 nmax(self.chunk_start[1], self.comment_pos[1])]
147 self.chunks_list.append(
148 util.ObjectDict(
149 { 'start': self.chunk_start,
150 'end': [nadd(self.last_change[0], 1),
151 nadd(self.last_change[1], 1)],
152 'comment_pos': comment_pos,
153 'additions': self.additions,
154 'removals': self.removals }))
155 self.reset()
156
157 def reset(self):
158 # This is called from __init__.
159 # pylint: disable=W0201
160 self.chunk_start = [None, None]
161 self.last_change = [None, None]
162 self.comment_pos = [None, None]
163 self.block_indent = None
164 self.block_closed = False
165 self.additions = False
166 self.removals = False
167 self.text_changed = False
168
169
170 def parse(lines):
171 """Helper function to parse lines to a list of chunks directly."""
172 parser = PolicyChangeParser(lines)
173 parser.run()
174 return parser.chunks_list
OLDNEW
« no previous file with comments | « reviewbot/handlers/policy_checklist/modification_comment.txt ('k') | reviewbot/handlers/policy_checklist/parser_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698