Skip to content

Commit 4cdac93

Browse files
committed
test ci fix
1 parent 836d8d7 commit 4cdac93

File tree

2 files changed

+92
-30
lines changed

2 files changed

+92
-30
lines changed

scripts/check_async.py

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,27 @@ def visit_ClassDef(self, node: ast.ClassDef) -> None:
2525
# Check if class has any async methods
2626
has_async = any(isinstance(item, ast.AsyncFunctionDef) for item in node.body)
2727

28-
# Check if it's a manager or client class
29-
is_async_class = (
30-
"Manager" in node.name
31-
or "Client" in node.name
32-
or "Suite" in node.name
33-
or "Realtime" in node.name
34-
or has_async
35-
)
28+
# Explicitly exclude utility classes that don't need to be async
29+
excluded_classes = [
30+
"ConfigManager", # Configuration management - sync utility
31+
"ErrorContext", # Error context manager - has async context but methods can be sync
32+
"Deprecation", # Deprecation utilities
33+
"Logger", # Logging utilities
34+
]
35+
36+
if node.name in excluded_classes:
37+
is_async_class = False
38+
else:
39+
# Check if it's a class that should be async based on patterns
40+
is_async_class = (
41+
("Manager" in node.name and node.name != "ConfigManager") # Managers except ConfigManager
42+
or "Client" in node.name
43+
or "Suite" in node.name
44+
or "Realtime" in node.name
45+
or "OrderBook" in node.name # OrderBook is async
46+
or "DataManager" in node.name # Data managers are async
47+
or has_async # Any class with async methods
48+
)
3649

3750
old_in_async = self.in_async_class
3851
if is_async_class:
@@ -71,33 +84,82 @@ def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
7184
self.generic_visit(node)
7285

7386
def _has_io_operations(self, node: ast.FunctionDef) -> bool:
74-
"""Check if function has I/O operations."""
87+
"""Check if function has actual I/O operations."""
88+
# Skip simple getter methods that just return values
89+
if node.name.startswith("get_") and self._is_simple_getter(node):
90+
return False
91+
7592
for item in ast.walk(node):
7693
if isinstance(item, ast.Call):
7794
if isinstance(item.func, ast.Attribute):
78-
# Check for common I/O operations
79-
if item.func.attr in [
80-
"get",
81-
"post",
82-
"put",
83-
"delete",
84-
"patch", # HTTP
85-
"connect",
86-
"send",
87-
"recv",
88-
"close", # Socket
89-
"read",
90-
"write",
91-
"open", # File I/O
92-
"execute",
93-
"fetch",
94-
"commit", # Database
95-
]:
95+
# Check for actual I/O operations on known I/O objects
96+
attr_name = item.func.attr
97+
98+
# Get the object being called on
99+
if isinstance(item.func.value, ast.Name):
100+
obj_name = item.func.value.id
101+
102+
# Check for known I/O objects and their methods
103+
if obj_name in ["httpx", "aiohttp", "requests", "urllib"]:
104+
if attr_name in ["get", "post", "put", "delete", "patch", "request"]:
105+
return True
106+
elif obj_name in ["socket", "websocket", "ws"]:
107+
if attr_name in ["connect", "send", "recv", "close"]:
108+
return True
109+
elif obj_name in ["file", "f", "fp"]:
110+
if attr_name in ["read", "write", "seek", "tell"]:
111+
return True
112+
elif obj_name in ["db", "database", "conn", "connection", "cursor"]:
113+
if attr_name in ["execute", "fetch", "commit", "rollback"]:
114+
return True
115+
116+
# Check for self.client or self.http calls (common in SDK)
117+
if isinstance(item.func.value, ast.Attribute):
118+
if hasattr(item.func.value, "attr"):
119+
obj_attr = item.func.value.attr
120+
if obj_attr in ["client", "http", "session", "api", "_client", "_http"]:
121+
if attr_name in ["get", "post", "put", "delete", "patch", "request", "fetch"]:
122+
return True
123+
124+
# Check for common async I/O patterns that should be async
125+
if attr_name in ["request", "fetch_data", "api_call", "send_request",
126+
"make_request", "http_get", "http_post"]:
96127
return True
128+
97129
elif isinstance(item.func, ast.Name):
98130
# Check for built-in I/O functions
99-
if item.func.id in ["open", "print"]:
131+
if item.func.id in ["open", "print", "input"]:
100132
return True
133+
# Check for common I/O function names
134+
if item.func.id in ["fetch", "request", "download", "upload"]:
135+
return True
136+
137+
return False
138+
139+
def _is_simple_getter(self, node: ast.FunctionDef) -> bool:
140+
"""Check if a method is a simple getter that doesn't perform I/O."""
141+
# Simple getters typically have a single return statement or simple logic
142+
if len(node.body) == 1 and isinstance(node.body[0], ast.Return):
143+
return True
144+
145+
# Check for simple property-like getters with basic logic
146+
has_io_call = False
147+
for item in ast.walk(node):
148+
if isinstance(item, ast.Call):
149+
# Check if any calls might be I/O
150+
if isinstance(item.func, ast.Attribute):
151+
if item.func.attr in ["request", "fetch", "api_call", "http_get", "http_post"]:
152+
has_io_call = True
153+
break
154+
elif isinstance(item.func, ast.Name):
155+
if item.func.id in ["open", "fetch", "request"]:
156+
has_io_call = True
157+
break
158+
159+
# If no I/O calls and the method is short, it's likely a simple getter
160+
if not has_io_call and len(node.body) <= 10:
161+
return True
162+
101163
return False
102164

103165

src/project_x_py/utils/error_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ async def async_wrapper(*args: Any, **kwargs: Any) -> T:
472472
f"{', '.join(missing_fields)}"
473473
)
474474

475-
return result
475+
return cast(T, result)
476476

477477
@functools.wraps(func)
478478
def sync_wrapper(*args: Any, **kwargs: Any) -> T:
@@ -494,7 +494,7 @@ def sync_wrapper(*args: Any, **kwargs: Any) -> T:
494494
f"{', '.join(missing_fields)}"
495495
)
496496

497-
return result
497+
return cast(T, result) # type: ignore[redundant-cast]
498498

499499
# Return appropriate wrapper based on function type
500500
if asyncio.iscoroutinefunction(func):

0 commit comments

Comments
 (0)