roma-debug 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,313 @@
1
+ """Core data models for ROMA Debug V2.
2
+
3
+ This module provides enhanced data structures for multi-language support
4
+ and deep debugging capabilities.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum
9
+ from typing import Optional, List, Any
10
+
11
+
12
+ class Language(Enum):
13
+ """Supported programming languages."""
14
+ PYTHON = "python"
15
+ JAVASCRIPT = "javascript"
16
+ TYPESCRIPT = "typescript"
17
+ GO = "go"
18
+ RUST = "rust"
19
+ JAVA = "java"
20
+ C = "c"
21
+ CPP = "cpp"
22
+ CSHARP = "csharp"
23
+ RUBY = "ruby"
24
+ PHP = "php"
25
+ UNKNOWN = "unknown"
26
+
27
+ @classmethod
28
+ def from_extension(cls, ext: str) -> "Language":
29
+ """Get language from file extension.
30
+
31
+ Args:
32
+ ext: File extension (with or without leading dot)
33
+
34
+ Returns:
35
+ Language enum value
36
+ """
37
+ ext = ext.lower().lstrip(".")
38
+ mapping = {
39
+ "py": cls.PYTHON,
40
+ "pyw": cls.PYTHON,
41
+ "pyi": cls.PYTHON,
42
+ "js": cls.JAVASCRIPT,
43
+ "mjs": cls.JAVASCRIPT,
44
+ "cjs": cls.JAVASCRIPT,
45
+ "jsx": cls.JAVASCRIPT,
46
+ "ts": cls.TYPESCRIPT,
47
+ "tsx": cls.TYPESCRIPT,
48
+ "mts": cls.TYPESCRIPT,
49
+ "cts": cls.TYPESCRIPT,
50
+ "go": cls.GO,
51
+ "rs": cls.RUST,
52
+ "java": cls.JAVA,
53
+ "c": cls.C,
54
+ "h": cls.C,
55
+ "cpp": cls.CPP,
56
+ "cc": cls.CPP,
57
+ "cxx": cls.CPP,
58
+ "hpp": cls.CPP,
59
+ "hxx": cls.CPP,
60
+ "cs": cls.CSHARP,
61
+ "rb": cls.RUBY,
62
+ "php": cls.PHP,
63
+ }
64
+ return mapping.get(ext, cls.UNKNOWN)
65
+
66
+
67
+ @dataclass
68
+ class Symbol:
69
+ """A code symbol (function, method, class, etc.).
70
+
71
+ Represents a named code construct that can contain the error line.
72
+ """
73
+ name: str
74
+ kind: str # 'function', 'method', 'class', 'module', etc.
75
+ start_line: int
76
+ end_line: int
77
+ start_col: int = 0
78
+ end_col: int = 0
79
+ parent: Optional["Symbol"] = None
80
+ children: List["Symbol"] = field(default_factory=list)
81
+ docstring: Optional[str] = None
82
+ decorators: List[str] = field(default_factory=list)
83
+
84
+ @property
85
+ def qualified_name(self) -> str:
86
+ """Get fully qualified name including parent chain."""
87
+ if self.parent:
88
+ return f"{self.parent.qualified_name}.{self.name}"
89
+ return self.name
90
+
91
+ def contains_line(self, line_number: int) -> bool:
92
+ """Check if this symbol contains the given line number."""
93
+ return self.start_line <= line_number <= self.end_line
94
+
95
+
96
+ @dataclass
97
+ class Import:
98
+ """Represents an import statement.
99
+
100
+ Tracks both the import syntax and resolved file path.
101
+ """
102
+ module_name: str # e.g., 'os.path', 'lodash', './utils'
103
+ alias: Optional[str] = None # e.g., 'np' for 'import numpy as np'
104
+ imported_names: List[str] = field(default_factory=list) # e.g., ['join', 'dirname']
105
+ is_relative: bool = False
106
+ relative_level: int = 0 # Number of dots for relative imports
107
+ line_number: int = 0
108
+ resolved_path: Optional[str] = None # Actual file path after resolution
109
+ language: Language = Language.UNKNOWN
110
+
111
+ @property
112
+ def full_import_string(self) -> str:
113
+ """Reconstruct the import statement."""
114
+ if self.language == Language.PYTHON:
115
+ if self.imported_names:
116
+ prefix = "." * self.relative_level if self.is_relative else ""
117
+ return f"from {prefix}{self.module_name} import {', '.join(self.imported_names)}"
118
+ elif self.alias:
119
+ return f"import {self.module_name} as {self.alias}"
120
+ else:
121
+ return f"import {self.module_name}"
122
+ elif self.language in (Language.JAVASCRIPT, Language.TYPESCRIPT):
123
+ if self.imported_names:
124
+ return f"import {{ {', '.join(self.imported_names)} }} from '{self.module_name}'"
125
+ elif self.alias:
126
+ return f"import {self.alias} from '{self.module_name}'"
127
+ else:
128
+ return f"import '{self.module_name}'"
129
+ elif self.language == Language.GO:
130
+ if self.alias:
131
+ return f'import {self.alias} "{self.module_name}"'
132
+ return f'import "{self.module_name}"'
133
+ return f"import {self.module_name}"
134
+
135
+
136
+ @dataclass
137
+ class FileContext:
138
+ """Extracted context from a source file.
139
+
140
+ This class is backward compatible with the original FileContext
141
+ while adding V2 fields for multi-language support.
142
+ """
143
+ filepath: str
144
+ line_number: int
145
+ context_type: str # 'ast', 'lines', 'missing', 'treesitter'
146
+ content: str
147
+ function_name: Optional[str] = None
148
+ class_name: Optional[str] = None
149
+ # V2 additions
150
+ language: Language = Language.UNKNOWN
151
+ imports: List[Import] = field(default_factory=list)
152
+ symbol: Optional[Symbol] = None
153
+ raw_source: Optional[str] = None # Full file source for later analysis
154
+
155
+ def to_dict(self) -> dict:
156
+ """Convert to dictionary (for JSON serialization)."""
157
+ return {
158
+ "filepath": self.filepath,
159
+ "line_number": self.line_number,
160
+ "context_type": self.context_type,
161
+ "content": self.content,
162
+ "function_name": self.function_name,
163
+ "class_name": self.class_name,
164
+ "language": self.language.value,
165
+ "imports": [
166
+ {
167
+ "module_name": imp.module_name,
168
+ "alias": imp.alias,
169
+ "imported_names": imp.imported_names,
170
+ "resolved_path": imp.resolved_path,
171
+ }
172
+ for imp in self.imports
173
+ ],
174
+ }
175
+
176
+
177
+ @dataclass
178
+ class TraceFrame:
179
+ """A single frame from a stack trace.
180
+
181
+ Represents one level in the call stack from an error traceback.
182
+ """
183
+ filepath: str
184
+ line_number: int
185
+ function_name: Optional[str] = None
186
+ column_number: Optional[int] = None
187
+ code_snippet: Optional[str] = None
188
+ language: Language = Language.UNKNOWN
189
+
190
+ def __str__(self) -> str:
191
+ parts = [f"{self.filepath}:{self.line_number}"]
192
+ if self.function_name:
193
+ parts.append(f" in {self.function_name}")
194
+ return "".join(parts)
195
+
196
+
197
+ @dataclass
198
+ class ParsedTraceback:
199
+ """A fully parsed traceback/stack trace.
200
+
201
+ Contains all frames from the error plus the error message.
202
+ """
203
+ frames: List[TraceFrame] = field(default_factory=list)
204
+ error_type: Optional[str] = None
205
+ error_message: Optional[str] = None
206
+ language: Language = Language.UNKNOWN
207
+ raw_traceback: str = ""
208
+
209
+ @property
210
+ def primary_frame(self) -> Optional[TraceFrame]:
211
+ """Get the primary frame (usually the last one where error occurred)."""
212
+ if self.frames:
213
+ return self.frames[-1]
214
+ return None
215
+
216
+ @property
217
+ def files(self) -> List[str]:
218
+ """Get list of unique file paths from all frames."""
219
+ seen = set()
220
+ result = []
221
+ for frame in self.frames:
222
+ if frame.filepath not in seen:
223
+ seen.add(frame.filepath)
224
+ result.append(frame.filepath)
225
+ return result
226
+
227
+
228
+ @dataclass
229
+ class UpstreamContext:
230
+ """Context from upstream modules (imports and callers).
231
+
232
+ Used for deep debugging to trace root causes beyond the traceback.
233
+ """
234
+ file_contexts: List[FileContext] = field(default_factory=list)
235
+ call_chain: List[str] = field(default_factory=list) # ["module_a.func1", "module_b.func2"]
236
+ relevant_definitions: dict = field(default_factory=dict) # {symbol: code}
237
+ dependency_summary: str = ""
238
+
239
+ def to_prompt_text(self) -> str:
240
+ """Format upstream context for inclusion in AI prompt."""
241
+ parts = []
242
+
243
+ if self.call_chain:
244
+ parts.append("## CALL CHAIN")
245
+ parts.append(" -> ".join(self.call_chain))
246
+
247
+ if self.relevant_definitions:
248
+ parts.append("\n## RELEVANT DEFINITIONS")
249
+ for symbol, code in self.relevant_definitions.items():
250
+ parts.append(f"\n### {symbol}")
251
+ parts.append(code)
252
+
253
+ if self.file_contexts:
254
+ parts.append("\n## UPSTREAM FILE CONTEXTS")
255
+ for ctx in self.file_contexts:
256
+ parts.append(f"\n### {ctx.filepath}")
257
+ parts.append(ctx.content)
258
+
259
+ if self.dependency_summary:
260
+ parts.append("\n## DEPENDENCY SUMMARY")
261
+ parts.append(self.dependency_summary)
262
+
263
+ return "\n".join(parts)
264
+
265
+
266
+ @dataclass
267
+ class AnalysisContext:
268
+ """Complete context for AI analysis.
269
+
270
+ Combines primary error context with traceback and upstream information.
271
+ """
272
+ primary_context: FileContext
273
+ traceback_contexts: List[FileContext] = field(default_factory=list)
274
+ upstream_context: Optional[UpstreamContext] = None
275
+ parsed_traceback: Optional[ParsedTraceback] = None
276
+ project_root: Optional[str] = None
277
+ error_analysis: Optional[object] = None # ErrorAnalysis from error_analyzer
278
+
279
+ def to_prompt_text(self) -> str:
280
+ """Format complete context for AI prompt."""
281
+ parts = []
282
+
283
+ # Primary error context
284
+ parts.append("## PRIMARY ERROR CONTEXT")
285
+ parts.append(f"File: {self.primary_context.filepath}")
286
+ parts.append(f"Line: {self.primary_context.line_number}")
287
+ if self.primary_context.function_name:
288
+ parts.append(f"Function: {self.primary_context.function_name}")
289
+ if self.primary_context.class_name:
290
+ parts.append(f"Class: {self.primary_context.class_name}")
291
+ parts.append(f"Language: {self.primary_context.language.value}")
292
+ parts.append("\n```")
293
+ parts.append(self.primary_context.content)
294
+ parts.append("```")
295
+
296
+ # Traceback contexts
297
+ if self.traceback_contexts:
298
+ parts.append("\n## TRACEBACK CONTEXTS")
299
+ for ctx in self.traceback_contexts:
300
+ if ctx.filepath != self.primary_context.filepath:
301
+ parts.append(f"\n### {ctx.filepath}:{ctx.line_number}")
302
+ if ctx.function_name:
303
+ parts.append(f"Function: {ctx.function_name}")
304
+ parts.append("```")
305
+ parts.append(ctx.content)
306
+ parts.append("```")
307
+
308
+ # Upstream context for deep debugging
309
+ if self.upstream_context:
310
+ parts.append("\n## UPSTREAM CONTEXT (Deep Debugging)")
311
+ parts.append(self.upstream_context.to_prompt_text())
312
+
313
+ return "\n".join(parts)