@@ -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
0 commit comments