ballpython 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,395 @@
1
+ """
2
+ Typeshed stub parser and symbol signature resolver.
3
+
4
+ Parses .pyi stub files, extracts typed function and method signatures,
5
+ and provides return types, argument types, and class attributes for
6
+ built-in and standard library symbols.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import ast
12
+ import os
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+ from typing import ClassVar
16
+
17
+
18
+ @dataclass(slots=True)
19
+ class FunctionSignature:
20
+ """Type signature for a function or method."""
21
+
22
+ name: str
23
+ param_types: dict[str, str] = field(default_factory=dict)
24
+ return_type: str = "Any"
25
+ is_method: bool = False
26
+ is_static: bool = False
27
+ is_class_method: bool = False
28
+
29
+
30
+ @dataclass(slots=True)
31
+ class ClassSignature:
32
+ """Type signature for a class definition."""
33
+
34
+ name: str
35
+ methods: dict[str, FunctionSignature] = field(default_factory=dict)
36
+ attributes: dict[str, str] = field(default_factory=dict)
37
+ base_classes: list[str] = field(default_factory=list)
38
+
39
+
40
+ class TypeshedResolver:
41
+ """Resolves type annotations and signatures from typeshed stubs and built-in catalogs."""
42
+
43
+ DEFAULT_TYPESHED_CANDIDATES: ClassVar[list[str]] = [
44
+ r"C:\Temp\pyrefly_bundled_typeshed_df16aab5f050",
45
+ ]
46
+
47
+ # Core built-in function return types (guaranteed baseline without file I/O)
48
+ BUILTIN_FUNCTIONS: ClassVar[dict[str, str]] = {
49
+ "len": "int",
50
+ "str": "str",
51
+ "int": "int",
52
+ "float": "float",
53
+ "bool": "bool",
54
+ "list": "list",
55
+ "dict": "dict",
56
+ "set": "set",
57
+ "tuple": "tuple",
58
+ "bytes": "bytes",
59
+ "abs": "int | float",
60
+ "all": "bool",
61
+ "any": "bool",
62
+ "bin": "str",
63
+ "hex": "str",
64
+ "oct": "str",
65
+ "chr": "str",
66
+ "ord": "int",
67
+ "hash": "int",
68
+ "id": "int",
69
+ "repr": "str",
70
+ "round": "int | float",
71
+ "sum": "int | float",
72
+ "min": "Any",
73
+ "max": "Any",
74
+ "open": "typing.IO[Any]",
75
+ "isinstance": "bool",
76
+ "issubclass": "bool",
77
+ "callable": "bool",
78
+ "hasattr": "bool",
79
+ "getattr": "Any",
80
+ "pow": "int | float",
81
+ "divmod": "tuple[int, int]",
82
+ }
83
+
84
+ # Core built-in method return types
85
+ BUILTIN_METHODS: ClassVar[dict[str, dict[str, str]]] = {
86
+ "str": {
87
+ "upper": "str",
88
+ "lower": "str",
89
+ "strip": "str",
90
+ "lstrip": "str",
91
+ "rstrip": "str",
92
+ "split": "list[str]",
93
+ "rsplit": "list[str]",
94
+ "splitlines": "list[str]",
95
+ "join": "str",
96
+ "replace": "str",
97
+ "startswith": "bool",
98
+ "endswith": "bool",
99
+ "find": "int",
100
+ "rfind": "int",
101
+ "index": "int",
102
+ "rindex": "int",
103
+ "count": "int",
104
+ "encode": "bytes",
105
+ "format": "str",
106
+ "isdigit": "bool",
107
+ "isalpha": "bool",
108
+ "isalnum": "bool",
109
+ "isspace": "bool",
110
+ "title": "str",
111
+ "capitalize": "str",
112
+ },
113
+ "list": {
114
+ "append": "None",
115
+ "extend": "None",
116
+ "insert": "None",
117
+ "remove": "None",
118
+ "pop": "Any",
119
+ "clear": "None",
120
+ "index": "int",
121
+ "count": "int",
122
+ "sort": "None",
123
+ "reverse": "None",
124
+ "copy": "list[Any]",
125
+ },
126
+ "dict": {
127
+ "get": "Any",
128
+ "keys": "typing.KeysView[Any]",
129
+ "values": "typing.ValuesView[Any]",
130
+ "items": "typing.ItemsView[Any, Any]",
131
+ "pop": "Any",
132
+ "popitem": "tuple[Any, Any]",
133
+ "clear": "None",
134
+ "update": "None",
135
+ "setdefault": "Any",
136
+ "copy": "dict[Any, Any]",
137
+ },
138
+ "set": {
139
+ "add": "None",
140
+ "remove": "None",
141
+ "discard": "None",
142
+ "pop": "Any",
143
+ "clear": "None",
144
+ "union": "set[Any]",
145
+ "intersection": "set[Any]",
146
+ "difference": "set[Any]",
147
+ "symmetric_difference": "set[Any]",
148
+ "issubset": "bool",
149
+ "issuperset": "bool",
150
+ "isdisjoint": "bool",
151
+ "copy": "set[Any]",
152
+ },
153
+ }
154
+
155
+ # Common standard library function signatures
156
+ STDLIB_FUNCTIONS: ClassVar[dict[str, dict[str, str]]] = {
157
+ "math": {
158
+ "sqrt": "float",
159
+ "ceil": "int",
160
+ "floor": "int",
161
+ "sin": "float",
162
+ "cos": "float",
163
+ "tan": "float",
164
+ "log": "float",
165
+ "exp": "float",
166
+ "radians": "float",
167
+ "degrees": "float",
168
+ },
169
+ "os": {
170
+ "getcwd": "str",
171
+ "listdir": "list[str]",
172
+ "system": "int",
173
+ },
174
+ "os.path": {
175
+ "join": "str",
176
+ "abspath": "str",
177
+ "basename": "str",
178
+ "dirname": "str",
179
+ "exists": "bool",
180
+ "isfile": "bool",
181
+ "isdir": "bool",
182
+ },
183
+ "json": {
184
+ "dumps": "str",
185
+ "loads": "Any",
186
+ },
187
+ }
188
+
189
+ def __init__(self, typeshed_path: str | Path | None = None) -> None:
190
+ self.typeshed_root: Path | None = self._locate_typeshed(typeshed_path)
191
+ self._parsed_stubs: dict[str, ast.Module] = {}
192
+ self._class_cache: dict[str, ClassSignature] = {}
193
+ self._func_cache: dict[str, FunctionSignature] = {}
194
+
195
+ def _locate_typeshed(self, custom_path: str | Path | None) -> Path | None:
196
+ if custom_path:
197
+ p = Path(custom_path).resolve()
198
+ if p.is_dir():
199
+ return p
200
+
201
+ for candidate in self.DEFAULT_TYPESHED_CANDIDATES:
202
+ cand_path = Path(candidate)
203
+ if cand_path.is_dir():
204
+ return cand_path
205
+
206
+ # Check environment variable
207
+ env_path = os.environ.get("TYPESHED_PATH")
208
+ if env_path:
209
+ p = Path(env_path).resolve()
210
+ if p.is_dir():
211
+ return p
212
+
213
+ return None
214
+
215
+ def get_builtin_return_type(self, func_name: str) -> str | None:
216
+ """Get return type for a built-in function."""
217
+ return self.BUILTIN_FUNCTIONS.get(func_name)
218
+
219
+ def get_builtin_method_return_type(
220
+ self, type_name: str, method_name: str
221
+ ) -> str | None:
222
+ """Get return type for a method on a built-in type."""
223
+ methods = self.BUILTIN_METHODS.get(type_name)
224
+ if methods:
225
+ return methods.get(method_name)
226
+ return None
227
+
228
+ def load_stub_for_module(self, module_name: str) -> ast.Module | None:
229
+ """Load and parse the .pyi stub for a module."""
230
+ if not self.typeshed_root:
231
+ return None
232
+
233
+ if module_name in self._parsed_stubs:
234
+ return self._parsed_stubs[module_name]
235
+
236
+ # Look for module.pyi or module/__init__.pyi
237
+ rel_parts = module_name.split(".")
238
+ candidate_file = self.typeshed_root.joinpath(*rel_parts).with_suffix(".pyi")
239
+ candidate_pkg = self.typeshed_root.joinpath(*rel_parts, "__init__.pyi")
240
+
241
+ target_file: Path | None = None
242
+ if candidate_file.is_file():
243
+ target_file = candidate_file
244
+ elif candidate_pkg.is_file():
245
+ target_file = candidate_pkg
246
+ elif len(rel_parts) == 1 and rel_parts[0] == "builtins":
247
+ builtins_stub = self.typeshed_root / "builtins.pyi"
248
+ if builtins_stub.is_file():
249
+ target_file = builtins_stub
250
+
251
+ if not target_file:
252
+ return None
253
+
254
+ try:
255
+ content = target_file.read_text(encoding="utf-8", errors="replace")
256
+ tree = ast.parse(content, filename=str(target_file))
257
+ self._parsed_stubs[module_name] = tree
258
+ return tree
259
+ except SyntaxError:
260
+ return None
261
+
262
+ @staticmethod
263
+ def _normalize_func_target(
264
+ module_name: str, func_name: str | None
265
+ ) -> tuple[str, str]:
266
+ if func_name is not None:
267
+ return module_name, func_name
268
+ if "." in module_name:
269
+ mod, fn = module_name.rsplit(".", 1)
270
+ return mod, fn
271
+ return "builtins", module_name
272
+
273
+ def _resolve_func_from_catalog(
274
+ self, module_name: str, func_name: str
275
+ ) -> FunctionSignature | None:
276
+ if module_name in ("builtins", "") and func_name in self.BUILTIN_FUNCTIONS:
277
+ return FunctionSignature(
278
+ name=func_name, return_type=self.BUILTIN_FUNCTIONS[func_name]
279
+ )
280
+ if (
281
+ module_name in self.STDLIB_FUNCTIONS
282
+ and func_name in self.STDLIB_FUNCTIONS[module_name]
283
+ ):
284
+ return FunctionSignature(
285
+ name=func_name,
286
+ return_type=self.STDLIB_FUNCTIONS[module_name][func_name],
287
+ )
288
+ return None
289
+
290
+ def _resolve_func_from_stub(
291
+ self, module_name: str, func_name: str
292
+ ) -> FunctionSignature | None:
293
+ tree = self.load_stub_for_module(module_name)
294
+ if not tree:
295
+ return None
296
+ for node in tree.body:
297
+ if (
298
+ isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
299
+ and node.name == func_name
300
+ ):
301
+ return self._extract_function_signature(node)
302
+ return None
303
+
304
+ def resolve_function(
305
+ self,
306
+ module_name: str,
307
+ func_name: str | None = None,
308
+ ) -> FunctionSignature | None:
309
+ """Resolve a function's typed signature from stubs or catalog tables."""
310
+ mod, fn = self._normalize_func_target(module_name, func_name)
311
+ cache_key = f"{mod}.{fn}"
312
+ if cache_key in self._func_cache:
313
+ return self._func_cache[cache_key]
314
+
315
+ sig = self._resolve_func_from_catalog(mod, fn) or self._resolve_func_from_stub(
316
+ mod, fn
317
+ )
318
+ if sig is not None:
319
+ self._func_cache[cache_key] = sig
320
+ return sig
321
+
322
+ def resolve_class(self, module_name: str, class_name: str) -> ClassSignature | None:
323
+ """Resolve a class's typed signature and member signatures from stubs."""
324
+ cache_key = f"{module_name}.{class_name}"
325
+ if cache_key in self._class_cache:
326
+ return self._class_cache[cache_key]
327
+
328
+ tree = self.load_stub_for_module(module_name)
329
+ if not tree:
330
+ return None
331
+
332
+ for node in tree.body:
333
+ if isinstance(node, ast.ClassDef) and node.name == class_name:
334
+ cls_sig = self._extract_class_signature(node)
335
+ self._class_cache[cache_key] = cls_sig
336
+ return cls_sig
337
+
338
+ return None
339
+
340
+ def _extract_function_signature(
341
+ self,
342
+ node: ast.FunctionDef | ast.AsyncFunctionDef,
343
+ is_method: bool = False,
344
+ ) -> FunctionSignature:
345
+ """Extract a FunctionSignature from an AST node."""
346
+ param_types: dict[str, str] = {}
347
+ all_args = node.args.posonlyargs + node.args.args + node.args.kwonlyargs
348
+ for arg in all_args:
349
+ if arg.annotation:
350
+ param_types[arg.arg] = ast.unparse(arg.annotation)
351
+ else:
352
+ param_types[arg.arg] = "Any"
353
+
354
+ return_type = "Any"
355
+ if node.returns:
356
+ return_type = ast.unparse(node.returns)
357
+
358
+ is_static = any(
359
+ isinstance(d, ast.Name) and d.id == "staticmethod"
360
+ for d in node.decorator_list
361
+ )
362
+ is_class = any(
363
+ isinstance(d, ast.Name) and d.id == "classmethod"
364
+ for d in node.decorator_list
365
+ )
366
+
367
+ return FunctionSignature(
368
+ name=node.name,
369
+ param_types=param_types,
370
+ return_type=return_type,
371
+ is_method=is_method,
372
+ is_static=is_static,
373
+ is_class_method=is_class,
374
+ )
375
+
376
+ def _extract_class_signature(self, node: ast.ClassDef) -> ClassSignature:
377
+ """Extract a ClassSignature from an AST ClassDef node."""
378
+ methods: dict[str, FunctionSignature] = {}
379
+ attributes: dict[str, str] = {}
380
+ base_classes: list[str] = [ast.unparse(b) for b in node.bases]
381
+
382
+ for stmt in node.body:
383
+ if isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef)):
384
+ methods[stmt.name] = self._extract_function_signature(
385
+ stmt, is_method=True
386
+ )
387
+ elif isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Name):
388
+ attributes[stmt.target.id] = ast.unparse(stmt.annotation)
389
+
390
+ return ClassSignature(
391
+ name=node.name,
392
+ methods=methods,
393
+ attributes=attributes,
394
+ base_classes=base_classes,
395
+ )