forked from PaddlePaddle/Paddle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
check_api_yaml_same.py
249 lines (203 loc) · 7.54 KB
/
check_api_yaml_same.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/usr/bin/env python
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import difflib
import os
import re
import sys
import yaml
root_path = sys.argv[4]
def read_yaml_ops():
ops_list = []
yaml_path = root_path + "/paddle/phi/api/yaml/ops.yaml"
legacy_yaml_path = root_path + "/paddle/phi/api/yaml/legacy_ops.yaml"
with open(yaml_path, 'r') as f:
ops_list = yaml.load(f, Loader=yaml.FullLoader)
with open(legacy_yaml_path, 'r') as f:
ops_list.extend(yaml.load(f, Loader=yaml.FullLoader))
return ops_list
def read_api(api_file):
with open(api_file, 'r') as f:
pr_apis = f.read()
pr_apis = pr_apis.splitlines()
result = []
for api in pr_apis:
# Delete all non-function api
if api.find('args') == -1:
continue
result.append(api)
return result
def get_api_args(api_item):
result = re.search(r"args=\[(?P<args>[^\]]*)\]", api_item)
result = [
param.strip().replace('\'', '')
for param in result.group('args').split(',')
]
if result[-1] == 'name':
result = result[:-1]
return result
def get_api_name(api_item):
if api_item[0] == '+' or api_item[0] == '-' or api_item[0] == ' ':
return api_item.split(" ")[1].split(".")[-1]
else:
return api_item.split(" ")[0].split(".")[-1]
def get_yaml_op_args(op_args):
args_list = op_args[1:-1].split(',')
args_list = [args.split('=')[0].strip() for args in args_list]
return [param.split(' ')[-1].strip() for param in args_list]
def get_api_diff(dev_api_file, pr_api_file):
develop_apis = read_api(dev_api_file)
pr_apis = read_api(pr_api_file)
differ = difflib.Differ()
diff_obj = differ.compare(develop_apis, pr_apis)
result = []
for each_diff in diff_obj:
result.append(each_diff)
return result
def get_yaml_diff(branch):
ops_yaml_path = root_path + "/paddle/phi/api/yaml/ops.yaml"
legacy_yaml_path = root_path + "/paddle/phi/api/yaml/legacy_ops.yaml"
git_cmd = (
"git diff -U0 upstream/"
+ branch
+ " "
+ ops_yaml_path
+ " "
+ legacy_yaml_path
)
yaml_diff = os.popen(git_cmd).readlines()
result = []
for line in yaml_diff:
result.append(line.strip('\r\n'))
return result
api_diffs = get_api_diff(sys.argv[1], sys.argv[2])
yaml_diffs = get_yaml_diff(sys.argv[3])
yaml_ops = read_yaml_ops() # The current PR yaml's ops
approve_api_msg = []
approve_yaml_msg = []
api_add = []
api_delete = []
for each_diff in api_diffs:
if each_diff[0] == '+':
api_add.append(each_diff)
if each_diff[0] == '-':
api_delete.append(each_diff)
# remove api that doesn't modify name and args
add_exclude = []
delete_exclude = []
for each_add_diff in api_add:
for each_delete_diff in api_delete:
if get_api_name(each_add_diff) == get_api_name(
each_delete_diff
) and get_api_args(each_add_diff) == get_api_args(each_delete_diff):
add_exclude.append(each_add_diff)
delete_exclude.append(each_delete_diff)
for exclude_item in add_exclude:
api_add.remove(exclude_item)
for exclude_item in delete_exclude:
api_delete.remove(exclude_item)
yaml_add = []
yaml_delete = []
for each_diff in yaml_diffs:
if each_diff[0] == '+':
yaml_add.append(each_diff)
if each_diff[0] == '-':
yaml_delete.append(each_diff)
# API add or modified
for each_add in api_add:
add = True
modify = False
need_approve = True
yaml_name_found = False
api_name = get_api_name(each_add)
api_args = get_api_args(each_add)
for each_delete_api in api_delete:
if get_api_name(each_delete_api) == api_name:
modify = True
add = False
# If we find yaml name in yaml_delete, it shows that
# yaml op's name is modified.
for each_delete_yaml in yaml_delete:
if each_delete_yaml.find(api_name) != -1:
yaml_name_found = True
for op in yaml_ops:
if op['op'] == api_name:
yaml_name_found = True
if api_args == get_yaml_op_args(op['args']):
need_approve = False
break
# If API is modified and doesn't have a corresponding yaml's op
# We needn't approve it
if modify and not yaml_name_found:
need_approve = False
# If API is added and yaml's op is not added,
# it shows that new api doesn't have a corresponding yaml's op.
# We needn't approve it
if add and len(yaml_add) == 0:
need_approve = False
# In others, the changes need to be approved.
# eg: 1, The args in api is inconsistent with yaml's op
# 2, New Api is add, but the yaml op's name may not be inconsistent
# with api's name.
# 3, Api's name is modified, but the yaml op's name is not modified.
if need_approve:
approve_api_msg.append(each_add)
# API delete
for each_delete in api_delete:
api_name = get_api_name(each_delete)
api_args = get_api_args(each_delete)
need_approve = False
for op in yaml_ops:
# When api is deleted, it is unusual to find the same name's
# op in yaml. So, we need review code.
if op['op'] == api_name and api_args == get_yaml_op_args(op['args']):
need_approve = True
break
if need_approve:
approve_api_msg.append(each_delete)
# For yaml, we don't have to consider its add or delete.
# Because if it is related with api, code above has dealt with it.
# But we need consider below situation:
# The op in yaml is modified and its corresponding api is not modified.
if len(api_add) == 0 and len(api_delete) == 0:
pr_apis = read_api(sys.argv[2]) # Get all api of this PR
for each_diff in yaml_delete:
# Note: The condition is relaxed, because symbol '-' can present delete and modification.
# So if op is deleted in yaml, code below is also triggered.
# But we mainly deal with modification here.
# If op name in yaml is modified and this name is in API,
# this PR need to be reviewed.
if each_diff.startswith('-- op'):
for api in pr_apis:
if each_diff.find(get_api_name(api)) != -1:
approve_yaml_msg.append(each_diff)
break
# if op args in yaml is modified and this args is in API,
# this PR need to be reviewed.
if each_diff.startswith('- args'):
yaml_op_args_str = each_diff.strip('- args : ')
yaml_op_args = get_yaml_op_args(yaml_op_args_str)
for api in pr_apis:
if get_api_args(api) == yaml_op_args:
approve_yaml_msg.append(each_diff)
break
# collect all msg
approve_msg = []
if len(approve_api_msg) != 0:
approve_msg = ['The APIs you changed are as follows:']
approve_msg.extend(approve_api_msg)
if len(approve_yaml_msg) != 0:
approve_msg = ['The Yaml File you changed are as follows:']
approve_msg.extend(approve_yaml_msg)
print('\r\n'.join(approve_msg))