461 Star 4.5K Fork 472

GVPPaddlePaddle/Paddle

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
check_api_compatible.py 6.42 KB
一键复制 编辑 原始数据 按行查看 历史
# Copyright (c) 2021 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 argparse
import inspect
import logging
import re
import sys
logger = logging.getLogger()
if logger.handlers:
# we assume the first handler is the one we want to configure
console = logger.handlers[0]
else:
console = logging.StreamHandler(sys.stderr)
logger.addHandler(console)
console.setFormatter(
logging.Formatter(
"%(asctime)s - %(funcName)s:%(lineno)d - %(levelname)s - %(message)s"
)
)
def _check_compatible(args_o, args_n, defaults_o, defaults_n):
# 如果参数减少了,需要提醒关注
if len(args_o) > len(args_n):
logger.debug("args num less then previous: %s vs %s", args_o, args_n)
return False
# 参数改名了,也要提醒关注
for idx in range(min(len(args_o), len(args_n))):
if args_o[idx] != args_n[idx]:
logger.debug(
"args's %d parameter diff with previous: %s vs %s",
idx,
args_o,
args_n,
)
return False
# 新增加了参数,必须提供默认值。以及不能减少默认值数量
if (len(args_n) - len(defaults_n)) > (len(args_o) - len(defaults_o)):
logger.debug(
"defaults num less then previous: %s vs %s", defaults_o, defaults_n
)
return False
# 默认值必须相等
for idx in range(min(len(defaults_o), len(defaults_n))):
nidx_o = -1 - idx
nidx_n = -1 - idx - (len(args_n) - len(args_o))
if defaults_o[nidx_o] != defaults_n[nidx_n]:
logger.debug(
"defaults's %d value diff with previous: %s vs %s",
nidx_n,
defaults_o,
defaults_n,
)
return False
return True
def check_compatible(old_api_spec, new_api_spec):
"""
check compatible, FullArgSpec
"""
if not (
isinstance(old_api_spec, inspect.FullArgSpec)
and isinstance(new_api_spec, inspect.FullArgSpec)
):
logger.warning(
"new_api_spec or old_api_spec is not instance of inspect.FullArgSpec"
)
return False
return _check_compatible(
old_api_spec.args,
new_api_spec.args,
[] if old_api_spec.defaults is None else old_api_spec.defaults,
[] if new_api_spec.defaults is None else new_api_spec.defaults,
)
def check_compatible_str(old_api_spec_str, new_api_spec_str):
patArgSpec = re.compile(
r'args=(.*), varargs=.*defaults=(None|\((.*)\)), kwonlyargs=.*'
)
mo_o = patArgSpec.search(old_api_spec_str)
mo_n = patArgSpec.search(new_api_spec_str)
if not (mo_o and mo_n):
# error
logger.warning("old_api_spec_str: %s", old_api_spec_str)
logger.warning("new_api_spec_str: %s", new_api_spec_str)
return False
args_o = eval(mo_o.group(1))
args_n = eval(mo_n.group(1))
defaults_o = mo_o.group(2) if mo_o.group(3) is None else mo_o.group(3)
defaults_n = mo_n.group(2) if mo_n.group(3) is None else mo_n.group(3)
defaults_o = defaults_o.split(', ') if defaults_o else []
defaults_n = defaults_n.split(', ') if defaults_n else []
return _check_compatible(args_o, args_n, defaults_o, defaults_n)
def read_argspec_from_file(specfile):
"""
read FullArgSpec from spec file
"""
res_dict = {}
patArgSpec = re.compile(
r'^(paddle[^,]+)\s+\((ArgSpec.*),\s\(\'document\W*([0-9a-z]{32})'
)
fullargspec_prefix = 'inspect.Full'
for line in specfile.readlines():
mo = patArgSpec.search(line)
if mo and mo.group(2) != 'ArgSpec()':
logger.debug("%s argspec: %s", mo.group(1), mo.group(2))
try:
res_dict[mo.group(1)] = eval(fullargspec_prefix + mo.group(2))
except: # SyntaxError, NameError:
res_dict[mo.group(1)] = fullargspec_prefix + mo.group(2)
return res_dict
arguments = [
# flags, dest, type, default, help
]
def parse_args():
"""
Parse input arguments
"""
global arguments
parser = argparse.ArgumentParser(
description='check api compatible across versions'
)
parser.add_argument('--debug', dest='debug', action="store_true")
parser.add_argument(
'prev',
type=argparse.FileType('r'),
help='the previous version (the version from develop branch)',
)
parser.add_argument(
'post',
type=argparse.FileType('r'),
help='the post version (the version from PullRequest)',
)
for item in arguments:
parser.add_argument(
item[0], dest=item[1], help=item[4], type=item[2], default=item[3]
)
if len(sys.argv) < 2:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if args.debug:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
if args.prev and args.post:
prev_spec = read_argspec_from_file(args.prev)
post_spec = read_argspec_from_file(args.post)
diff_api_names = []
for as_post_name, as_post in post_spec.items():
as_prev = prev_spec.get(as_post_name)
if as_prev is None: # the api is deleted
continue
if isinstance(as_prev, str) or isinstance(as_post, str):
as_prev_str = (
as_prev if isinstance(as_prev, str) else repr(as_prev)
)
as_post_str = (
as_post if isinstance(as_post, str) else repr(as_post)
)
if not check_compatible_str(as_prev_str, as_post_str):
diff_api_names.append(as_post_name)
else:
if not check_compatible(as_prev, as_post):
diff_api_names.append(as_post_name)
if diff_api_names:
print('\n'.join(diff_api_names))
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/paddlepaddle/Paddle.git
git@gitee.com:paddlepaddle/Paddle.git
paddlepaddle
Paddle
Paddle
develop

搜索帮助