more-compute 0.1.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,17 @@
1
+ """
2
+ Utility modules for MoreCompute notebook
3
+ """
4
+
5
+ from .python_environment_util import PythonEnvironmentDetector
6
+ from .system_environment_util import DeviceMetrics
7
+ from .error_utils import ErrorUtils
8
+ from .cache_util import make_cache_key
9
+ from .notebook_util import coerce_cell_source
10
+
11
+ __all__ = [
12
+ 'PythonEnvironmentDetector',
13
+ 'DeviceMetrics',
14
+ 'ErrorUtils',
15
+ 'make_cache_key',
16
+ 'coerce_cell_source'
17
+ ]
@@ -0,0 +1,23 @@
1
+ import hashlib
2
+ import json
3
+
4
+
5
+ def make_cache_key(prefix: str, **params) -> str:
6
+ """
7
+ Create a cache key from parameters.
8
+
9
+ Args:
10
+ prefix: Cache key prefix (e.g., "gpu_avail", "pod_list")
11
+ **params: Parameters to hash into the key
12
+
13
+ Returns:
14
+ Cache key string in format "prefix:hash"
15
+
16
+ Example:
17
+ >>> make_cache_key("gpu_avail", regions=["us"], gpu_type="H100")
18
+ "gpu_avail:a3f2c1e8"
19
+ """
20
+ # Sort params for consistency: {a:1, b:2} == {b:2, a:1}
21
+ param_str = json.dumps(params, sort_keys=True, default=str)
22
+ hash_val = hashlib.md5(param_str.encode()).hexdigest()[:8]
23
+ return f"{prefix}:{hash_val}"
@@ -0,0 +1,322 @@
1
+ import re
2
+ import traceback
3
+ from typing import Dict, Any, List
4
+
5
+ """
6
+
7
+ most of this is ai-generated, but I would like custom error handling for most general errors
8
+
9
+ imagine like user does not know pip is ran with !pip install, and just runs pip install,
10
+
11
+ it would be nice just to have a custom error message pointing user to use !pip rather than just fail, etc
12
+
13
+
14
+
15
+ """
16
+
17
+
18
+
19
+ def create_enhanced_error_info(exception: Exception, traceback_lines: List[str]) -> Dict[str, Any]:
20
+ """
21
+ Create enhanced error information with better formatting and suggestions
22
+
23
+ Args:
24
+ exception: The caught exception
25
+ traceback_lines: List of traceback lines
26
+
27
+ Returns:
28
+ Enhanced error dictionary with suggestions and formatting
29
+ """
30
+ error_type = type(exception).__name__
31
+ error_message = str(exception)
32
+
33
+ # Check for specific error types and provide custom handling
34
+ if _is_pip_related_error(error_message, traceback_lines):
35
+ return _create_pip_error_info(error_type, error_message, traceback_lines)
36
+ elif _is_import_error(error_type, error_message):
37
+ return _create_import_error_info(error_type, error_message, traceback_lines)
38
+ elif _is_file_not_found_error(error_type, error_message):
39
+ return _create_file_error_info(error_type, error_message, traceback_lines)
40
+ else:
41
+ return _create_generic_enhanced_error(error_type, error_message, traceback_lines)
42
+
43
+
44
+ def _is_pip_related_error(error_message: str, traceback_lines: List[str]) -> bool:
45
+ """Check if the error is related to pip operations"""
46
+ pip_indicators = [
47
+ "pip install",
48
+ "pip uninstall",
49
+ "pip show",
50
+ "pip list",
51
+ "subprocess.*pip",
52
+ "ModuleNotFoundError.*pip",
53
+ "No module named 'pip'"
54
+ ]
55
+
56
+ combined_text = error_message + " " + " ".join(traceback_lines)
57
+
58
+ # Check for direct pip command syntax errors (most common case)
59
+ if "SyntaxError" in error_message and "pip install" in combined_text:
60
+ return True
61
+
62
+ return any(re.search(indicator, combined_text, re.IGNORECASE) for indicator in pip_indicators)
63
+
64
+
65
+ def _is_import_error(error_type: str, error_message: str) -> bool:
66
+ """Check if this is a module import error"""
67
+ return error_type in ["ModuleNotFoundError", "ImportError"]
68
+
69
+
70
+ def _is_file_not_found_error(error_type: str, error_message: str) -> bool:
71
+ """Check if this is a file not found error"""
72
+ return error_type == "FileNotFoundError"
73
+
74
+
75
+ def _create_pip_error_info(error_type: str, error_message: str, traceback_lines: List[str]) -> Dict[str, Any]:
76
+ """Create custom error info for pip-related errors"""
77
+
78
+ # Extract package name if possible
79
+ package_name = _extract_package_name_from_pip_command(error_message, traceback_lines)
80
+
81
+ suggestions = []
82
+ enhanced_message = error_message
83
+
84
+ # Handle direct pip command syntax errors (most common case)
85
+ if "SyntaxError" in error_type and "pip install" in " ".join(traceback_lines):
86
+ enhanced_message = "Cannot run pip commands directly in Python code"
87
+ if package_name:
88
+ suggestions.extend([
89
+ f"Use shell command instead: !pip install {package_name}",
90
+ f"Or use subprocess: subprocess.run(['pip', 'install', '{package_name}'])",
91
+ "Note: pip commands need to be run in shell, not Python"
92
+ ])
93
+ else:
94
+ suggestions.extend([
95
+ "Use shell command instead: !pip install <package_name>",
96
+ "Or use subprocess: subprocess.run(['pip', 'install', '<package_name>'])",
97
+ "Note: pip commands need to be run in shell, not Python"
98
+ ])
99
+ elif "ModuleNotFoundError" in error_type and package_name:
100
+ enhanced_message = f"Package '{package_name}' is not installed"
101
+ suggestions.extend([
102
+ f"Install the package: !pip install {package_name}",
103
+ f"Or use subprocess: subprocess.run(['pip', 'install', '{package_name}'])",
104
+ "Check if the package name is spelled correctly",
105
+ "Verify the package exists on PyPI: https://pypi.org/"
106
+ ])
107
+ elif "pip install" in error_message.lower():
108
+ suggestions.extend([
109
+ "Try using shell command: !pip install <package_name>",
110
+ "Check your internet connection",
111
+ "Verify the package name exists on PyPI",
112
+ "Try upgrading pip: !pip install --upgrade pip"
113
+ ])
114
+ elif "No module named 'pip'" in error_message:
115
+ enhanced_message = "pip is not installed or not found in your Python environment"
116
+ suggestions.extend([
117
+ "Reinstall pip: python -m ensurepip --upgrade",
118
+ "Or download get-pip.py and run: python get-pip.py",
119
+ "Check if you're using the correct Python environment"
120
+ ])
121
+
122
+ return {
123
+ "ename": "PipError",
124
+ "evalue": enhanced_message,
125
+ "suggestions": suggestions,
126
+ "traceback": _format_traceback(traceback_lines),
127
+ "error_type": "pip_error"
128
+ }
129
+
130
+
131
+ def _create_import_error_info(error_type: str, error_message: str, traceback_lines: List[str]) -> Dict[str, Any]:
132
+ """Create enhanced error info for import errors"""
133
+
134
+ # Extract module name
135
+ module_name = _extract_module_name(error_message)
136
+
137
+ suggestions = []
138
+ if module_name:
139
+ suggestions.extend([
140
+ f"Install the missing package: !pip install {module_name}",
141
+ "Check if the module name is spelled correctly",
142
+ "Verify the module is in your Python path",
143
+ f"Search for the correct package name: https://pypi.org/search/?q={module_name}"
144
+ ])
145
+
146
+ return {
147
+ "ename": error_type,
148
+ "evalue": error_message,
149
+ "suggestions": suggestions,
150
+ "traceback": _format_traceback(traceback_lines),
151
+ "error_type": "import_error"
152
+ }
153
+
154
+
155
+ def _create_file_error_info(error_type: str, error_message: str, traceback_lines: List[str]) -> Dict[str, Any]:
156
+ """Create enhanced error info for file errors"""
157
+
158
+ # Extract file path if possible
159
+ file_path = _extract_file_path(error_message)
160
+
161
+ suggestions = [
162
+ "Check if the file path is correct",
163
+ "Verify the file exists in the specified location",
164
+ "Check file permissions",
165
+ "Use absolute path instead of relative path"
166
+ ]
167
+
168
+ if file_path:
169
+ suggestions.insert(0, f"File not found: {file_path}")
170
+
171
+ return {
172
+ "ename": error_type,
173
+ "evalue": error_message,
174
+ "suggestions": suggestions,
175
+ "traceback": _format_traceback(traceback_lines),
176
+ "error_type": "file_error"
177
+ }
178
+
179
+
180
+ def _create_generic_enhanced_error(error_type: str, error_message: str, traceback_lines: List[str]) -> Dict[str, Any]:
181
+ """Create enhanced error info for generic errors"""
182
+
183
+ suggestions = _generate_generic_suggestions(error_type, error_message)
184
+
185
+ return {
186
+ "ename": error_type,
187
+ "evalue": error_message,
188
+ "suggestions": suggestions,
189
+ "traceback": _format_traceback(traceback_lines),
190
+ "error_type": "generic_error"
191
+ }
192
+
193
+
194
+ def _extract_package_name(error_message: str, traceback_lines: List[str]) -> str:
195
+ """Extract package name from error message or traceback"""
196
+ # Try to find module name in "No module named 'xyz'" pattern
197
+ match = re.search(r"No module named ['\"]([^'\"]+)['\"]", error_message)
198
+ if match:
199
+ return match.group(1)
200
+
201
+ # Try to find in traceback
202
+ combined_text = " ".join(traceback_lines)
203
+ match = re.search(r"import\s+(\w+)", combined_text)
204
+ if match:
205
+ return match.group(1)
206
+
207
+ return ""
208
+
209
+
210
+ def _extract_package_name_from_pip_command(error_message: str, traceback_lines: List[str]) -> str:
211
+ """Extract package name from pip install command in traceback"""
212
+ combined_text = error_message + " " + " ".join(traceback_lines)
213
+
214
+ # Look for "pip install package-name" pattern
215
+ match = re.search(r"pip\s+install\s+([\w-]+)", combined_text)
216
+ if match:
217
+ return match.group(1)
218
+
219
+ # Fallback to regular package name extraction
220
+ return _extract_package_name(error_message, traceback_lines)
221
+
222
+
223
+ def _extract_module_name(error_message: str) -> str:
224
+ """Extract module name from import error message"""
225
+ match = re.search(r"No module named ['\"]([^'\"]+)['\"]", error_message)
226
+ return match.group(1) if match else ""
227
+
228
+
229
+ def _extract_file_path(error_message: str) -> str:
230
+ """Extract file path from file error message"""
231
+ # Try to find file path in brackets or quotes
232
+ patterns = [
233
+ r"\[Errno \d+\] .+?: '([^']+)'",
234
+ r'"([^"]+)".*not found',
235
+ r"'([^']+)'.*not found"
236
+ ]
237
+
238
+ for pattern in patterns:
239
+ match = re.search(pattern, error_message)
240
+ if match:
241
+ return match.group(1)
242
+
243
+ return ""
244
+
245
+
246
+ def _generate_generic_suggestions(error_type: str, error_message: str) -> List[str]:
247
+ """Generate helpful suggestions for generic errors"""
248
+ suggestions = []
249
+
250
+ if error_type == "SyntaxError":
251
+ suggestions.extend([
252
+ "Check for missing parentheses, brackets, or quotes",
253
+ "Verify proper indentation",
254
+ "Look for typos in keywords or variable names"
255
+ ])
256
+ elif error_type == "NameError":
257
+ suggestions.extend([
258
+ "Check if the variable is defined before use",
259
+ "Verify variable name spelling",
260
+ "Make sure to import required modules"
261
+ ])
262
+ elif error_type == "TypeError":
263
+ suggestions.extend([
264
+ "Check function arguments and their types",
265
+ "Verify object methods and attributes",
266
+ "Check if you're calling a function correctly"
267
+ ])
268
+ elif error_type == "ValueError":
269
+ suggestions.extend([
270
+ "Check input values and their formats",
271
+ "Verify numeric conversions",
272
+ "Check if values are within expected ranges"
273
+ ])
274
+ elif error_type == "KeyError":
275
+ suggestions.extend([
276
+ "Check if the key exists in the dictionary",
277
+ "Use .get() method for safer key access",
278
+ "Verify the key spelling and type"
279
+ ])
280
+ elif error_type == "IndexError":
281
+ suggestions.extend([
282
+ "Check list/array bounds",
283
+ "Verify the index is within range",
284
+ "Check if the list is empty"
285
+ ])
286
+ else:
287
+ suggestions.extend([
288
+ "Check the error message for specific details",
289
+ "Look at the line number in the traceback",
290
+ "Search online for this specific error type"
291
+ ])
292
+
293
+ return suggestions
294
+
295
+
296
+ def _format_traceback(traceback_lines: List[str]) -> List[str]:
297
+ """Format traceback lines for better readability"""
298
+ if not traceback_lines:
299
+ return []
300
+
301
+ # Remove empty lines and clean up formatting
302
+ formatted_lines = []
303
+ for line in traceback_lines:
304
+ if line.strip():
305
+ formatted_lines.append(line.rstrip())
306
+
307
+ # Limit to last 15 lines for readability
308
+ if len(formatted_lines) > 15:
309
+ formatted_lines = ["... (traceback truncated) ..."] + formatted_lines[-15:]
310
+
311
+ return formatted_lines
312
+
313
+
314
+ class ErrorUtils:
315
+ """Helper class to generate enhanced error payloads for the frontend."""
316
+
317
+ def format_exception(self, exception: Exception) -> Dict[str, Any]:
318
+ """Return enhanced error information for the given exception."""
319
+
320
+ formatted_traceback = traceback.format_exception(type(exception), exception, exception.__traceback__)
321
+ cleaned_traceback = [line.rstrip("\n") for line in formatted_traceback]
322
+ return create_enhanced_error_info(exception, cleaned_traceback)
@@ -0,0 +1,44 @@
1
+ """Notebook utility functions for cell processing."""
2
+
3
+
4
+ def coerce_cell_source(value):
5
+ """
6
+ Coerce various cell source formats to a string.
7
+
8
+ Handles:
9
+ - None → empty string
10
+ - str → unchanged
11
+ - bytes/bytearray → decoded to UTF-8
12
+ - list → joined into single string
13
+
14
+ Args:
15
+ value: Cell source in various formats
16
+
17
+ Returns:
18
+ String representation of cell source
19
+ """
20
+ if value is None:
21
+ return ''
22
+ if isinstance(value, str):
23
+ return value
24
+ if isinstance(value, (bytes, bytearray)):
25
+ try:
26
+ return value.decode('utf-8') # type: ignore[arg-type]
27
+ except Exception:
28
+ return value.decode('utf-8', errors='ignore') # type: ignore[arg-type]
29
+ if isinstance(value, list):
30
+ parts = []
31
+ for item in value:
32
+ if item is None:
33
+ continue
34
+ if isinstance(item, str):
35
+ parts.append(item)
36
+ elif isinstance(item, (bytes, bytearray)):
37
+ try:
38
+ parts.append(item.decode('utf-8')) # type: ignore[arg-type]
39
+ except Exception:
40
+ parts.append(item.decode('utf-8', errors='ignore')) # type: ignore[arg-type]
41
+ else:
42
+ parts.append(str(item))
43
+ return ''.join(parts)
44
+ return str(value)
@@ -0,0 +1,197 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import subprocess
5
+ import platform
6
+ from pathlib import Path
7
+
8
+
9
+ class PythonEnvironmentDetector:
10
+ """Detects Python environments (system Python and conda)"""
11
+
12
+ def __init__(self):
13
+ self.system = platform.system().lower()
14
+
15
+ def detect_all_environments(self) -> list[dict[str, str]]:
16
+ """Detect all Python environments on the system"""
17
+ environments = []
18
+
19
+ try:
20
+ # 1. Conda environments (check first for proper naming)
21
+ environments.extend(self._detect_conda_environments())
22
+ except Exception as e:
23
+ print(f"Warning: Conda detection failed: {e}")
24
+
25
+ try:
26
+ # 2. System Python installations
27
+ environments.extend(self._detect_system_python())
28
+ except Exception as e:
29
+ print(f"Warning: System Python detection failed: {e}")
30
+
31
+ try:
32
+ # 3. Check for venv in current directory
33
+ environments.extend(self._detect_local_venv())
34
+ except Exception as e:
35
+ print(f"Warning: Local venv detection failed: {e}")
36
+
37
+ # Remove duplicates based on path (keep first occurrence)
38
+ seen_paths = set()
39
+ unique_environments = []
40
+ for env in environments:
41
+ if env['path'] not in seen_paths:
42
+ seen_paths.add(env['path'])
43
+ unique_environments.append(env)
44
+
45
+ return sorted(unique_environments, key=lambda x: x['name'])
46
+
47
+ def _detect_system_python(self) -> list[dict[str, str]]:
48
+ """Detect system Python installations"""
49
+ environments = []
50
+
51
+ # Common Python executable names
52
+ python_names = ['python3', 'python']
53
+
54
+ if self.system == 'windows':
55
+ python_names.extend(['py', 'python.exe'])
56
+
57
+ for python_name in python_names:
58
+ try:
59
+ cmd = 'where' if self.system == 'windows' else 'which'
60
+ result = subprocess.run([cmd, python_name],
61
+ capture_output=True, text=True, timeout=5)
62
+
63
+ if result.returncode == 0:
64
+ python_path = result.stdout.strip().split('\n')[0]
65
+ version = self._get_python_version(python_path)
66
+
67
+ if version:
68
+ environments.append({
69
+ 'name': f'System Python ({python_name})',
70
+ 'path': python_path,
71
+ 'version': version,
72
+ 'type': 'system',
73
+ 'active': python_path == sys.executable
74
+ })
75
+
76
+ except (subprocess.TimeoutExpired, FileNotFoundError):
77
+ continue
78
+
79
+ return environments
80
+
81
+ def _detect_conda_environments(self) -> list[dict[str, str]]:
82
+ """Detect Conda/Mamba environments"""
83
+ environments = []
84
+
85
+ # Try conda first, then mamba
86
+ for cmd in ['conda', 'mamba']:
87
+ try:
88
+ result = subprocess.run([cmd, 'env', 'list', '--json'],
89
+ capture_output=True, text=True, timeout=10)
90
+
91
+ if result.returncode == 0:
92
+ data = json.loads(result.stdout)
93
+ root_prefix = data.get('root_prefix', '')
94
+
95
+ for env_path in data.get('envs', []):
96
+ # Find python in conda env
97
+ env_path_obj = Path(env_path)
98
+ if self.system == 'windows':
99
+ python_path = env_path_obj / 'python.exe'
100
+ else:
101
+ python_path = env_path_obj / 'bin' / 'python'
102
+
103
+ if python_path.exists() and python_path.is_file():
104
+ # Check if this is the base/root environment
105
+ if env_path == root_prefix:
106
+ env_name = f'{cmd} (base)'
107
+ else:
108
+ env_name = os.path.basename(env_path)
109
+
110
+ version = self._get_python_version(str(python_path))
111
+ if version:
112
+ environments.append({
113
+ 'name': env_name,
114
+ 'path': str(python_path),
115
+ 'version': version,
116
+ 'type': 'conda',
117
+ 'active': str(python_path) == sys.executable
118
+ })
119
+ break
120
+
121
+ except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError):
122
+ continue
123
+
124
+ return environments
125
+
126
+ def _detect_local_venv(self) -> list[dict[str, str]]:
127
+ """Detect virtual environments in current directory only"""
128
+ environments = []
129
+
130
+ # Only check common venv names in current directory
131
+ venv_names = ['.venv', 'venv']
132
+
133
+ for venv_name in venv_names:
134
+ venv_path = Path.cwd() / venv_name
135
+ if venv_path.exists() and venv_path.is_dir():
136
+ # Find python executable
137
+ if self.system == 'windows':
138
+ python_path = venv_path / 'Scripts' / 'python.exe'
139
+ else:
140
+ python_path = venv_path / 'bin' / 'python'
141
+
142
+ if python_path.exists() and python_path.is_file():
143
+ version = self._get_python_version(str(python_path))
144
+ if version:
145
+ environments.append({
146
+ 'name': venv_name,
147
+ 'path': str(python_path),
148
+ 'version': version,
149
+ 'type': 'venv',
150
+ 'active': str(python_path) == sys.executable
151
+ })
152
+
153
+ return environments
154
+
155
+ def _get_python_version(self, python_path: str) -> str | None:
156
+ """Get Python version from executable"""
157
+ try:
158
+ result = subprocess.run([python_path, '--version'],
159
+ capture_output=True, text=True, timeout=5)
160
+
161
+ if result.returncode == 0:
162
+ # Parse "Python 3.11.4" -> "3.11.4"
163
+ version_line = result.stdout.strip()
164
+ if version_line.startswith('Python '):
165
+ return version_line[7:] # Remove "Python "
166
+
167
+ except (subprocess.TimeoutExpired, FileNotFoundError):
168
+ pass
169
+
170
+ return None
171
+
172
+ def get_current_environment(self) -> dict[str, str]:
173
+ """Get information about the currently active Python environment"""
174
+ return {
175
+ 'name': 'Current Python',
176
+ 'path': sys.executable,
177
+ 'version': f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
178
+ 'type': 'current',
179
+ 'active': True
180
+ }
181
+
182
+
183
+ # Example usage
184
+ if __name__ == "__main__":
185
+ detector = PythonEnvironmentDetector()
186
+
187
+ print("Detecting Python environments...")
188
+ environments = detector.detect_all_environments()
189
+
190
+ print(f"\nFound {len(environments)} Python environments:")
191
+ print("-" * 60)
192
+
193
+ for env in environments:
194
+ status = "ACTIVE" if env['active'] else ""
195
+ print(f"{env['name']:<25} Python {env['version']:<8} {env['type']:<8} {status}")
196
+ print(f"{'':25} {env['path']}")
197
+ print()