diff --git a/tools/count_api_without_ops.py b/tools/count_api_without_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..84dd9a6b2f63b7e5cb63c6ab4367a2591274d660 --- /dev/null +++ b/tools/count_api_without_ops.py @@ -0,0 +1,140 @@ +# Copyright (c) 2020 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. +""" +List all operator-raleated APIs that contains append_op but not core.ops.xx. + +Usage: + python ./count_api_without_ops.py paddle +""" +from __future__ import print_function + +import importlib +import inspect +import collections +import sys +import pydoc +import hashlib +import six +import functools + +visited_modules = set() + +# APIs that should not be printed into API.spec +omitted_list = [ + "paddle.fluid.LoDTensor.set", # Do not know why it should be omitted + "paddle.fluid.io.ComposeNotAligned", + "paddle.fluid.io.ComposeNotAligned.__init__", +] + +api_with_ops = [] +api_without_ops = [] + + +def queue_dict(member, cur_name): + if cur_name in omitted_list: + return + + if inspect.isclass(member): + pass + else: + try: + source = inspect.getsource(member) + if source.find('append_op') != -1: + if source.find('core.ops') != -1: + api_with_ops.append(cur_name) + else: + api_without_ops.append(cur_name) + + except Exception as e: # special for PyBind method + pass + + +def visit_member(parent_name, member): + cur_name = ".".join([parent_name, member.__name__]) + if inspect.isclass(member): + queue_dict(member, cur_name) + for name, value in inspect.getmembers(member): + if hasattr(value, '__name__') and (not name.startswith("_") or + name == "__init__"): + visit_member(cur_name, value) + elif inspect.ismethoddescriptor(member): + return + elif callable(member): + queue_dict(member, cur_name) + elif inspect.isgetsetdescriptor(member): + return + else: + raise RuntimeError("Unsupported generate signature of member, type {0}". + format(str(type(member)))) + + +def is_primitive(instance): + int_types = (int, long) if six.PY2 else (int, ) + pritimitive_types = int_types + (float, str) + if isinstance(instance, pritimitive_types): + return True + elif isinstance(instance, (list, tuple, set)): + for obj in instance: + if not is_primitive(obj): + return False + + return True + else: + return False + + +def visit_all_module(mod): + mod_name = mod.__name__ + if mod_name != 'paddle' and not mod_name.startswith('paddle.'): + return + + if mod_name.startswith('paddle.fluid.core'): + return + + if mod in visited_modules: + return + + visited_modules.add(mod) + + for member_name in ( + name + for name in (mod.__all__ if hasattr(mod, "__all__") else dir(mod)) + if not name.startswith("_")): + instance = getattr(mod, member_name, None) + if instance is None: + continue + + if is_primitive(instance): + continue + + if not hasattr(instance, "__name__"): + continue + + if inspect.ismodule(instance): + visit_all_module(instance) + else: + visit_member(mod.__name__, instance) + + +modules = sys.argv[1].split(",") +for m in modules: + visit_all_module(importlib.import_module(m)) + +print('api_with_ops:', len(api_with_ops)) +print('\n'.join(api_with_ops)) + +print('\n==============\n') + +print('api_without_ops:', len(api_without_ops)) +print('\n'.join(api_without_ops))