Merge pull request #5097 from wumode/refector-check_method

This commit is contained in:
jxxghp
2025-11-05 23:15:24 +08:00
committed by GitHub
3 changed files with 72 additions and 29 deletions
+28 -29
View File
@@ -1,5 +1,7 @@
import ast
import dis import dis
import inspect import inspect
import textwrap
from types import FunctionType from types import FunctionType
from typing import Any, Callable, get_type_hints from typing import Any, Callable, get_type_hints
@@ -39,45 +41,42 @@ class ObjectUtils:
return len(list(parameters.keys())) return len(list(parameters.keys()))
@staticmethod @staticmethod
def check_method(func: FunctionType) -> bool: def check_method(func: Callable[..., Any]) -> bool:
""" """
检查函数是否已实现 检查函数是否已实现
""" """
try: try:
# 尝试通过源代码分析 src = inspect.getsource(func)
source = inspect.getsource(func) tree = ast.parse(textwrap.dedent(src))
in_comment = False node = tree.body[0]
for line in source.split('\n'): if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
line = line.strip() return True
# 跳过空行 body = node.body
if not line:
for stmt in body:
# 跳过 pass
if isinstance(stmt, ast.Pass):
continue continue
# 处理"""单行注释 # 跳过 docstring 或 ...
if (line.startswith(('"""', "'''")) if isinstance(stmt, ast.Expr):
and line.endswith(('"""', "'''")) expr = stmt.value
and len(line) > 3): if isinstance(expr, ast.Constant):
continue if isinstance(expr.value, str) or expr.value is Ellipsis:
# 处理"""多行注释 continue
if line.startswith(('"""', "'''")): # 检查 raise NotImplementedError
in_comment = not in_comment if isinstance(stmt, ast.Raise):
continue exc = stmt.exc
# 在注释中则跳过 if isinstance(exc, ast.Call) and getattr(exc.func, "id", None) == "NotImplementedError":
if in_comment: continue
continue if isinstance(exc, ast.Name) and exc.id == "NotImplementedError":
# 跳过#注释、pass语句、装饰器、函数定义行 continue
if (line.startswith('#')
or line == "pass"
or line.startswith('@')
or line.startswith('def ')):
continue
# 发现有效代码行
return True return True
# 没有有效代码行
return False return False
except Exception as err: except Exception as err:
print(err) print(err)
# 源代码分析失败时,进行字节码分析 # 源代码分析失败时,进行字节码分析
code_obj = func.__code__ code_obj = func.__code__ # type: ignore[attr-defined]
instructions = list(dis.get_instructions(code_obj)) instructions = list(dis.get_instructions(code_obj))
# 检查是否为仅返回None的简单结构 # 检查是否为仅返回None的简单结构
if len(instructions) == 2: if len(instructions) == 2:
+3
View File
@@ -1,6 +1,8 @@
import unittest import unittest
from tests.test_metainfo import MetaInfoTest from tests.test_metainfo import MetaInfoTest
from tests.test_object import ObjectUtilsTest
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestSuite() suite = unittest.TestSuite()
@@ -8,6 +10,7 @@ if __name__ == '__main__':
# 测试名称识别 # 测试名称识别
suite.addTest(MetaInfoTest('test_metainfo')) suite.addTest(MetaInfoTest('test_metainfo'))
suite.addTest(MetaInfoTest('test_emby_format_ids')) suite.addTest(MetaInfoTest('test_emby_format_ids'))
suite.addTest(ObjectUtilsTest('test_check_method'))
# 运行测试 # 运行测试
runner = unittest.TextTestRunner() runner = unittest.TextTestRunner()
+41
View File
@@ -0,0 +1,41 @@
from unittest import TestCase
from app.utils.object import ObjectUtils
class ObjectUtilsTest(TestCase):
def test_check_method(self):
def implemented_function():
return "Hello"
def pass_function():
pass
def docstring_function():
"""This is a docstring."""
def ellipsis_function():
...
def not_implemented_function():
raise NotImplementedError
def not_implemented_function_with_call():
raise NotImplementedError()
async def multiple_lines_async_def(_param1: str,
_param2: str):
pass
def empty_function():
return
self.assertTrue(ObjectUtils.check_method(implemented_function))
self.assertFalse(ObjectUtils.check_method(pass_function))
self.assertFalse(ObjectUtils.check_method(docstring_function))
self.assertFalse(ObjectUtils.check_method(ellipsis_function))
self.assertFalse(ObjectUtils.check_method(not_implemented_function))
self.assertFalse(ObjectUtils.check_method(not_implemented_function_with_call))
self.assertFalse(ObjectUtils.check_method(multiple_lines_async_def))
self.assertTrue(ObjectUtils.check_method(empty_function))