()
| 179 | * Get available variables in the Python namespace |
| 180 | */ |
| 181 | export async function getPythonVariables(): Promise<Record<string, any>> { |
| 182 | const pyodide = getPyodide(); |
| 183 | if (!pyodide) { |
| 184 | return {}; |
| 185 | } |
| 186 | |
| 187 | try { |
| 188 | const variables = await pyodide.runPython(` |
| 189 | import json |
| 190 | import pandas as pd |
| 191 | import numpy as np |
| 192 | |
| 193 | vars_info = {} |
| 194 | # Create a snapshot to avoid "dictionary changed size during iteration" error |
| 195 | globals_snapshot = dict(globals()) |
| 196 | for name, obj in globals_snapshot.items(): |
| 197 | if not name.startswith('_') and not callable(obj): |
| 198 | try: |
| 199 | if isinstance(obj, pd.DataFrame): |
| 200 | vars_info[name] = { |
| 201 | 'type': 'DataFrame', |
| 202 | 'shape': obj.shape, |
| 203 | 'columns': obj.columns.tolist() |
| 204 | } |
| 205 | elif isinstance(obj, np.ndarray): |
| 206 | vars_info[name] = { |
| 207 | 'type': 'ndarray', |
| 208 | 'shape': obj.shape, |
| 209 | 'dtype': str(obj.dtype) |
| 210 | } |
| 211 | elif isinstance(obj, (list, tuple, dict)): |
| 212 | vars_info[name] = { |
| 213 | 'type': type(obj).__name__, |
| 214 | 'length': len(obj) if hasattr(obj, '__len__') else None |
| 215 | } |
| 216 | else: |
| 217 | vars_info[name] = { |
| 218 | 'type': type(obj).__name__, |
| 219 | 'value': str(obj)[:100] # Truncate long values |
| 220 | } |
| 221 | except: |
| 222 | pass # Skip problematic objects |
| 223 | |
| 224 | json.dumps(vars_info) |
| 225 | `); |
| 226 | |
| 227 | return JSON.parse(variables); |
| 228 | } catch (error) { |
| 229 | console.error('[Python] Failed to get variables:', error); |
| 230 | return {}; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Clear Python namespace (keep imports) |
no test coverage detected