Skip to content

Commit facc89b

Browse files
committed
1. add check_recursion to morebuiltins.funcs, to check if a function is recursive.
1 parent 3788f80 commit facc89b

File tree

4 files changed

+43
-2
lines changed

4 files changed

+43
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### 1.3.1 (2025-06-19)
2+
1. add `check_recursion` to `morebuiltins.funcs`, to check if a function is recursive.
3+
14
### 1.3.0 (2025-03-08)
25
1. **Compatibility Warning**. rename `morebuiltins.functools` to `morebuiltins.funcs` to avoid conflict with `functools` in python standard library.
36
2. add `key_type` arg to `sqlite.KV`, to support `int` key.

morebuiltins/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.3.0"
1+
__version__ = "1.3.1"
22
__all__ = [
33
"morebuiltins.utils",
44
"morebuiltins.date",

morebuiltins/cmd/log_server.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import asyncio
22
import json
33
import logging
4-
import logging.handlers
54
import os
65
import signal
76
import sys

morebuiltins/funcs.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ast
12
import asyncio
23
import importlib
34
import importlib.util
@@ -44,6 +45,7 @@
4445
"RotatingFileWriter",
4546
"get_function",
4647
"to_thread",
48+
"check_recursion",
4749
]
4850

4951

@@ -1204,6 +1206,43 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):
12041206
async_logger = AsyncQueueListener
12051207

12061208

1209+
def check_recursion(function: Callable, return_error=False):
1210+
"""Check if a function is recursive by inspecting its AST.
1211+
Returns True if the function calls itself, otherwise False.
1212+
1213+
Demo::
1214+
>>> def recursive_func():
1215+
... return recursive_func()
1216+
>>> check_recursion(recursive_func)
1217+
True
1218+
>>> def non_recursive_func():
1219+
... return 1 + 1
1220+
>>> check_recursion(non_recursive_func)
1221+
False
1222+
>>> # print is a std-lib function
1223+
>>> check_recursion(print, return_error=False)
1224+
>>> type(check_recursion(print, return_error=True))
1225+
<class 'TypeError'>
1226+
"""
1227+
try:
1228+
source = inspect.getsource(function)
1229+
tree = ast.parse(source)
1230+
func_name = function.__name__
1231+
for node in ast.walk(tree):
1232+
if (
1233+
isinstance(node, ast.Call)
1234+
and isinstance(node.func, ast.Name)
1235+
and node.func.id == func_name
1236+
):
1237+
return True
1238+
return False
1239+
except Exception as e:
1240+
if return_error:
1241+
return e
1242+
else:
1243+
return None
1244+
1245+
12071246
def test_bg_task():
12081247
async def _test_bg_task():
12091248
async def coro():

0 commit comments

Comments
 (0)