pygeai 0.6.0b6__py3-none-any.whl → 0.6.0b10__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.
- pygeai/_docs/source/conf.py +78 -6
- pygeai/_docs/source/content/api_reference/admin.rst +161 -0
- pygeai/_docs/source/content/api_reference/assistant.rst +326 -0
- pygeai/_docs/source/content/api_reference/auth.rst +379 -0
- pygeai/_docs/source/content/api_reference/embeddings.rst +31 -1
- pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
- pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
- pygeai/_docs/source/content/api_reference/files.rst +592 -0
- pygeai/_docs/source/content/api_reference/gam.rst +401 -0
- pygeai/_docs/source/content/api_reference/health.rst +58 -0
- pygeai/_docs/source/content/api_reference/project.rst +20 -18
- pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
- pygeai/_docs/source/content/api_reference/rerank.rst +94 -0
- pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
- pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
- pygeai/_docs/source/content/api_reference.rst +13 -1
- pygeai/_docs/source/content/debugger.rst +376 -83
- pygeai/_docs/source/content/migration.rst +528 -0
- pygeai/_docs/source/content/modules.rst +1 -1
- pygeai/_docs/source/index.rst +59 -7
- pygeai/_docs/source/pygeai.auth.rst +29 -0
- pygeai/_docs/source/pygeai.cli.commands.rst +16 -0
- pygeai/_docs/source/pygeai.cli.rst +8 -0
- pygeai/_docs/source/pygeai.core.utils.rst +16 -0
- pygeai/_docs/source/pygeai.rst +1 -0
- pygeai/_docs/source/pygeai.tests.auth.rst +21 -0
- pygeai/_docs/source/pygeai.tests.cli.commands.rst +16 -0
- pygeai/_docs/source/pygeai.tests.cli.rst +16 -0
- pygeai/_docs/source/pygeai.tests.core.base.rst +8 -0
- pygeai/_docs/source/pygeai.tests.core.embeddings.rst +16 -0
- pygeai/_docs/source/pygeai.tests.core.files.rst +8 -0
- pygeai/_docs/source/pygeai.tests.core.plugins.rst +21 -0
- pygeai/_docs/source/pygeai.tests.core.rst +1 -0
- pygeai/_docs/source/pygeai.tests.evaluation.dataset.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.plan.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.result.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.rst +20 -0
- pygeai/_docs/source/pygeai.tests.integration.lab.processes.rst +8 -0
- pygeai/_docs/source/pygeai.tests.organization.rst +8 -0
- pygeai/_docs/source/pygeai.tests.rst +2 -0
- pygeai/_docs/source/pygeai.tests.snippets.auth.rst +10 -0
- pygeai/_docs/source/pygeai.tests.snippets.chat.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +45 -0
- pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +197 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +133 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +37 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +10 -0
- pygeai/_docs/source/pygeai.tests.snippets.organization.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.rst +2 -0
- pygeai/admin/clients.py +12 -32
- pygeai/assistant/clients.py +16 -44
- pygeai/assistant/data/clients.py +1 -0
- pygeai/assistant/data_analyst/clients.py +6 -13
- pygeai/assistant/rag/clients.py +24 -67
- pygeai/auth/clients.py +88 -14
- pygeai/auth/endpoints.py +4 -0
- pygeai/chat/clients.py +192 -25
- pygeai/chat/endpoints.py +2 -1
- pygeai/cli/commands/auth.py +178 -2
- pygeai/cli/commands/chat.py +227 -1
- pygeai/cli/commands/embeddings.py +56 -8
- pygeai/cli/commands/lab/ai_lab.py +0 -2
- pygeai/cli/commands/migrate.py +994 -434
- pygeai/cli/commands/organization.py +241 -0
- pygeai/cli/error_handler.py +116 -0
- pygeai/cli/geai.py +28 -10
- pygeai/cli/parsers.py +8 -2
- pygeai/core/base/clients.py +4 -1
- pygeai/core/common/exceptions.py +11 -10
- pygeai/core/embeddings/__init__.py +19 -0
- pygeai/core/embeddings/clients.py +20 -9
- pygeai/core/embeddings/mappers.py +16 -2
- pygeai/core/embeddings/responses.py +9 -2
- pygeai/core/feedback/clients.py +4 -8
- pygeai/core/files/clients.py +10 -25
- pygeai/core/files/managers.py +42 -0
- pygeai/core/llm/clients.py +11 -26
- pygeai/core/models.py +107 -0
- pygeai/core/plugins/clients.py +4 -7
- pygeai/core/rerank/clients.py +4 -8
- pygeai/core/secrets/clients.py +14 -37
- pygeai/core/services/rest.py +1 -1
- pygeai/core/utils/parsers.py +32 -0
- pygeai/core/utils/validators.py +10 -0
- pygeai/dbg/__init__.py +3 -0
- pygeai/dbg/debugger.py +565 -70
- pygeai/evaluation/clients.py +2 -1
- pygeai/evaluation/dataset/clients.py +46 -44
- pygeai/evaluation/plan/clients.py +28 -26
- pygeai/evaluation/result/clients.py +38 -5
- pygeai/gam/clients.py +10 -25
- pygeai/health/clients.py +4 -7
- pygeai/lab/agents/clients.py +21 -54
- pygeai/lab/agents/endpoints.py +2 -0
- pygeai/lab/clients.py +1 -0
- pygeai/lab/models.py +3 -3
- pygeai/lab/processes/clients.py +45 -127
- pygeai/lab/strategies/clients.py +11 -25
- pygeai/lab/tools/clients.py +23 -67
- pygeai/lab/tools/endpoints.py +3 -0
- pygeai/migration/__init__.py +31 -0
- pygeai/migration/strategies.py +404 -155
- pygeai/migration/tools.py +170 -3
- pygeai/organization/clients.py +135 -51
- pygeai/organization/endpoints.py +6 -1
- pygeai/organization/limits/clients.py +32 -91
- pygeai/organization/managers.py +157 -1
- pygeai/organization/mappers.py +76 -2
- pygeai/organization/responses.py +25 -1
- pygeai/proxy/clients.py +4 -1
- pygeai/tests/admin/test_clients.py +16 -11
- pygeai/tests/assistants/rag/test_clients.py +35 -23
- pygeai/tests/assistants/test_clients.py +22 -15
- pygeai/tests/auth/test_clients.py +191 -7
- pygeai/tests/chat/test_clients.py +211 -1
- pygeai/tests/cli/commands/test_embeddings.py +32 -9
- pygeai/tests/cli/commands/test_evaluation.py +7 -0
- pygeai/tests/cli/commands/test_migrate.py +112 -243
- pygeai/tests/cli/test_error_handler.py +225 -0
- pygeai/tests/cli/test_geai_driver.py +154 -0
- pygeai/tests/cli/test_parsers.py +5 -5
- pygeai/tests/core/embeddings/test_clients.py +144 -0
- pygeai/tests/core/embeddings/test_managers.py +171 -0
- pygeai/tests/core/embeddings/test_mappers.py +142 -0
- pygeai/tests/core/feedback/test_clients.py +2 -0
- pygeai/tests/core/files/test_clients.py +1 -0
- pygeai/tests/core/llm/test_clients.py +14 -9
- pygeai/tests/core/plugins/test_clients.py +5 -3
- pygeai/tests/core/rerank/test_clients.py +1 -0
- pygeai/tests/core/secrets/test_clients.py +19 -13
- pygeai/tests/dbg/test_debugger.py +453 -75
- pygeai/tests/evaluation/dataset/test_clients.py +3 -1
- pygeai/tests/evaluation/plan/test_clients.py +4 -2
- pygeai/tests/evaluation/result/test_clients.py +7 -5
- pygeai/tests/gam/test_clients.py +1 -1
- pygeai/tests/health/test_clients.py +1 -0
- pygeai/tests/lab/agents/test_clients.py +9 -0
- pygeai/tests/lab/processes/test_clients.py +36 -0
- pygeai/tests/lab/processes/test_mappers.py +3 -0
- pygeai/tests/lab/strategies/test_clients.py +14 -9
- pygeai/tests/migration/test_strategies.py +45 -218
- pygeai/tests/migration/test_tools.py +133 -9
- pygeai/tests/organization/limits/test_clients.py +17 -0
- pygeai/tests/organization/test_clients.py +206 -1
- pygeai/tests/organization/test_managers.py +122 -1
- pygeai/tests/proxy/test_clients.py +2 -0
- pygeai/tests/proxy/test_integration.py +1 -0
- pygeai/tests/snippets/auth/__init__.py +0 -0
- pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
- pygeai/tests/snippets/chat/get_response.py +15 -0
- pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
- pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
- pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
- pygeai/tests/snippets/dbg/__init__.py +0 -0
- pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
- pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
- pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
- pygeai/tests/snippets/dbg/stepping_example.py +40 -0
- pygeai/tests/snippets/embeddings/cache_example.py +31 -0
- pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
- pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
- pygeai/tests/snippets/embeddings/openai_example.py +30 -0
- pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
- pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
- pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
- pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
- pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
- pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
- pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
- pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
- pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
- pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
- pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
- pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
- pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
- pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
- pygeai/tests/snippets/migrate/__init__.py +45 -0
- pygeai/tests/snippets/migrate/agent_migration.py +110 -0
- pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
- pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
- pygeai/tests/snippets/migrate/process_migration.py +64 -0
- pygeai/tests/snippets/migrate/project_migration.py +42 -0
- pygeai/tests/snippets/migrate/tool_migration.py +64 -0
- pygeai/tests/snippets/organization/create_project.py +2 -2
- pygeai/tests/snippets/organization/get_memberships.py +12 -0
- pygeai/tests/snippets/organization/get_organization_members.py +6 -0
- pygeai/tests/snippets/organization/get_project_members.py +6 -0
- pygeai/tests/snippets/organization/get_project_memberships.py +12 -0
- pygeai/tests/snippets/organization/get_project_roles.py +6 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/RECORD +227 -124
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/top_level.txt +0 -0
pygeai/dbg/debugger.py
CHANGED
|
@@ -1,12 +1,47 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import sys
|
|
3
3
|
import inspect
|
|
4
|
-
|
|
4
|
+
import readline
|
|
5
|
+
import pprint
|
|
6
|
+
from types import FrameType
|
|
7
|
+
from typing import Optional, Any, Callable, Set, Dict, List, Tuple
|
|
8
|
+
from dataclasses import dataclass, field
|
|
5
9
|
|
|
6
10
|
from pygeai.cli.geai import main as geai
|
|
7
11
|
from pygeai.core.utils.console import Console
|
|
8
12
|
|
|
9
13
|
|
|
14
|
+
@dataclass
|
|
15
|
+
class Breakpoint:
|
|
16
|
+
"""Represents a breakpoint with optional conditions."""
|
|
17
|
+
module: Optional[str] = None
|
|
18
|
+
function_name: Optional[str] = None
|
|
19
|
+
condition: Optional[str] = None
|
|
20
|
+
enabled: bool = True
|
|
21
|
+
hit_count: int = 0
|
|
22
|
+
|
|
23
|
+
def __hash__(self):
|
|
24
|
+
return hash((self.module, self.function_name))
|
|
25
|
+
|
|
26
|
+
def __eq__(self, other):
|
|
27
|
+
if not isinstance(other, Breakpoint):
|
|
28
|
+
return False
|
|
29
|
+
return self.module == other.module and self.function_name == other.function_name
|
|
30
|
+
|
|
31
|
+
def matches(self, module: str, func_name: str) -> bool:
|
|
32
|
+
"""Check if this breakpoint matches the given module and function."""
|
|
33
|
+
if not self.enabled:
|
|
34
|
+
return False
|
|
35
|
+
module_match = self.module is None or self.module == module
|
|
36
|
+
func_match = self.function_name is None or self.function_name == func_name
|
|
37
|
+
return module_match and func_match
|
|
38
|
+
|
|
39
|
+
def __str__(self):
|
|
40
|
+
status = "enabled" if self.enabled else "disabled"
|
|
41
|
+
cond = f" [if {self.condition}]" if self.condition else ""
|
|
42
|
+
return f"{self.module or '*'}:{self.function_name or '*'} ({status}, hits: {self.hit_count}){cond}"
|
|
43
|
+
|
|
44
|
+
|
|
10
45
|
class Debugger:
|
|
11
46
|
"""
|
|
12
47
|
A debugger for the GEAI application to trace and control execution flow.
|
|
@@ -15,124 +50,584 @@ class Debugger:
|
|
|
15
50
|
and pause execution at specified breakpoints. Breakpoints can be set for specific modules or functions, allowing
|
|
16
51
|
developers to inspect local variables, execute arbitrary code in the current context, and control program flow
|
|
17
52
|
through an interactive command interface.
|
|
53
|
+
|
|
54
|
+
Features:
|
|
55
|
+
- Module filtering for performance (only traces pygeai modules by default)
|
|
56
|
+
- Breakpoint management (add, list, remove, enable/disable, conditional)
|
|
57
|
+
- Stack navigation (up/down frames)
|
|
58
|
+
- Stepping (step-into, step-over, step-out)
|
|
59
|
+
- Variable inspection with pretty-printing
|
|
60
|
+
- Source code display
|
|
61
|
+
- Stack trace viewing
|
|
62
|
+
- Readline support for command history
|
|
18
63
|
"""
|
|
19
64
|
|
|
20
|
-
def __init__(self):
|
|
21
|
-
|
|
22
|
-
|
|
65
|
+
def __init__(self, target: Optional[Callable] = None, module_filter: str = "pygeai"):
|
|
66
|
+
"""
|
|
67
|
+
Initialize the debugger.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
target: The callable to debug. If None, defaults to pygeai.cli.geai.main
|
|
71
|
+
module_filter: Only trace modules starting with this prefix (for performance)
|
|
72
|
+
"""
|
|
73
|
+
self.target = target or geai
|
|
74
|
+
self.module_filter = module_filter
|
|
75
|
+
self._setup_logging()
|
|
76
|
+
self.logger = logging.getLogger('geai.dbg')
|
|
77
|
+
|
|
78
|
+
self.breakpoints: Dict[Tuple[Optional[str], Optional[str]], Breakpoint] = {}
|
|
23
79
|
self.paused: bool = False
|
|
24
|
-
|
|
25
|
-
|
|
80
|
+
self.current_frame: Optional[FrameType] = None
|
|
81
|
+
self.frame_stack: List[FrameType] = []
|
|
82
|
+
self.current_frame_index: int = 0
|
|
83
|
+
|
|
84
|
+
# Stepping state
|
|
85
|
+
self.step_mode: Optional[str] = None # 'step', 'next', 'return', 'until'
|
|
86
|
+
self.step_frame: Optional[FrameType] = None
|
|
87
|
+
self.step_depth: int = 0
|
|
88
|
+
self.current_depth: int = 0
|
|
89
|
+
|
|
90
|
+
# Setup readline for command history
|
|
91
|
+
self._setup_readline()
|
|
92
|
+
|
|
93
|
+
self.logger.info("GEAI debugger started.")
|
|
94
|
+
self.logger.debug(f"Module filter: {self.module_filter}")
|
|
26
95
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
logger.
|
|
96
|
+
def _setup_logging(self):
|
|
97
|
+
"""Setup logging configuration, avoiding duplicate handlers."""
|
|
98
|
+
logger = logging.getLogger('geai.dbg')
|
|
99
|
+
|
|
100
|
+
# Only setup if not already configured
|
|
101
|
+
if not logger.handlers:
|
|
102
|
+
logger.setLevel(logging.DEBUG)
|
|
103
|
+
console_handler = logging.StreamHandler()
|
|
104
|
+
console_handler.setLevel(logging.DEBUG)
|
|
105
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
106
|
+
console_handler.setFormatter(formatter)
|
|
107
|
+
logger.addHandler(console_handler)
|
|
108
|
+
logger.propagate = False
|
|
30
109
|
|
|
31
|
-
|
|
32
|
-
|
|
110
|
+
def _setup_readline(self):
|
|
111
|
+
"""Setup readline for command history and tab completion."""
|
|
112
|
+
try:
|
|
113
|
+
import os
|
|
114
|
+
histfile = os.path.expanduser("~/.geai_dbg_history")
|
|
115
|
+
try:
|
|
116
|
+
readline.read_history_file(histfile)
|
|
117
|
+
readline.set_history_length(1000)
|
|
118
|
+
except FileNotFoundError:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
import atexit
|
|
122
|
+
atexit.register(readline.write_history_file, histfile)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
self.logger.debug(f"Could not setup readline: {e}")
|
|
33
125
|
|
|
34
|
-
|
|
35
|
-
|
|
126
|
+
def reset(self):
|
|
127
|
+
"""Reset debugger state."""
|
|
128
|
+
self.breakpoints.clear()
|
|
129
|
+
self.paused = False
|
|
130
|
+
self.current_frame = None
|
|
131
|
+
self.frame_stack.clear()
|
|
132
|
+
self.current_frame_index = 0
|
|
133
|
+
self.step_mode = None
|
|
134
|
+
self.step_frame = None
|
|
135
|
+
self.step_depth = 0
|
|
136
|
+
self.current_depth = 0
|
|
137
|
+
self.logger.info("Debugger state reset.")
|
|
36
138
|
|
|
37
|
-
|
|
139
|
+
def add_breakpoint(
|
|
140
|
+
self,
|
|
141
|
+
module: Optional[str] = None,
|
|
142
|
+
function_name: Optional[str] = None,
|
|
143
|
+
condition: Optional[str] = None
|
|
144
|
+
) -> Breakpoint:
|
|
145
|
+
"""
|
|
146
|
+
Add a breakpoint by module and/or function name.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
module: Module name to break on (None for any)
|
|
150
|
+
function_name: Function name to break on (None for any)
|
|
151
|
+
condition: Optional condition expression (Python code)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The created Breakpoint object
|
|
155
|
+
"""
|
|
156
|
+
key = (module, function_name)
|
|
157
|
+
if key in self.breakpoints:
|
|
158
|
+
bp = self.breakpoints[key]
|
|
159
|
+
self.logger.warning(f"Breakpoint already exists: {bp}")
|
|
160
|
+
return bp
|
|
161
|
+
|
|
162
|
+
bp = Breakpoint(module=module, function_name=function_name, condition=condition)
|
|
163
|
+
self.breakpoints[key] = bp
|
|
164
|
+
self.logger.info(f"Breakpoint added: {bp}")
|
|
165
|
+
return bp
|
|
38
166
|
|
|
39
|
-
def
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
|
|
167
|
+
def remove_breakpoint(self, module: Optional[str] = None, function_name: Optional[str] = None) -> bool:
|
|
168
|
+
"""Remove a breakpoint."""
|
|
169
|
+
key = (module, function_name)
|
|
170
|
+
if key in self.breakpoints:
|
|
171
|
+
bp = self.breakpoints.pop(key)
|
|
172
|
+
self.logger.info(f"Breakpoint removed: {bp}")
|
|
173
|
+
return True
|
|
174
|
+
self.logger.warning(f"Breakpoint not found: {module or '*'}:{function_name or '*'}")
|
|
175
|
+
return False
|
|
43
176
|
|
|
44
|
-
def
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
return self.trace_function
|
|
177
|
+
def list_breakpoints(self) -> List[Breakpoint]:
|
|
178
|
+
"""List all breakpoints."""
|
|
179
|
+
return list(self.breakpoints.values())
|
|
48
180
|
|
|
181
|
+
def enable_breakpoint(self, module: Optional[str] = None, function_name: Optional[str] = None) -> bool:
|
|
182
|
+
"""Enable a breakpoint."""
|
|
183
|
+
key = (module, function_name)
|
|
184
|
+
if key in self.breakpoints:
|
|
185
|
+
self.breakpoints[key].enabled = True
|
|
186
|
+
self.logger.info(f"Breakpoint enabled: {self.breakpoints[key]}")
|
|
187
|
+
return True
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
def disable_breakpoint(self, module: Optional[str] = None, function_name: Optional[str] = None) -> bool:
|
|
191
|
+
"""Disable a breakpoint."""
|
|
192
|
+
key = (module, function_name)
|
|
193
|
+
if key in self.breakpoints:
|
|
194
|
+
self.breakpoints[key].enabled = False
|
|
195
|
+
self.logger.info(f"Breakpoint disabled: {self.breakpoints[key]}")
|
|
196
|
+
return True
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
def clear_breakpoints(self):
|
|
200
|
+
"""Clear all breakpoints."""
|
|
201
|
+
count = len(self.breakpoints)
|
|
202
|
+
self.breakpoints.clear()
|
|
203
|
+
self.logger.info(f"Cleared {count} breakpoint(s).")
|
|
204
|
+
|
|
205
|
+
def _should_trace_module(self, module: Optional[str]) -> bool:
|
|
206
|
+
"""Check if we should trace this module based on filter."""
|
|
207
|
+
if not module:
|
|
208
|
+
return False
|
|
209
|
+
return module.startswith(self.module_filter)
|
|
210
|
+
|
|
211
|
+
def _check_condition(self, bp: Breakpoint, frame: FrameType) -> bool:
|
|
212
|
+
"""Check if breakpoint condition is met."""
|
|
213
|
+
if not bp.condition:
|
|
214
|
+
return True
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
# Evaluate condition in frame's context
|
|
218
|
+
result = eval(bp.condition, frame.f_globals, frame.f_locals)
|
|
219
|
+
return bool(result)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
self.logger.error(f"Error evaluating breakpoint condition '{bp.condition}': {e}")
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
def _build_frame_stack(self, frame: FrameType) -> List[FrameType]:
|
|
225
|
+
"""Build a list of frames from current to top of stack."""
|
|
226
|
+
stack = []
|
|
227
|
+
current = frame
|
|
228
|
+
while current is not None:
|
|
229
|
+
stack.append(current)
|
|
230
|
+
current = current.f_back
|
|
231
|
+
return stack
|
|
232
|
+
|
|
233
|
+
def trace_function(self, frame: FrameType, event: str, arg: Any) -> Optional[Callable]:
|
|
234
|
+
"""Trace function calls to intercept execution."""
|
|
49
235
|
module = frame.f_globals.get('__name__')
|
|
236
|
+
|
|
237
|
+
# Performance optimization: only trace filtered modules
|
|
238
|
+
if not self._should_trace_module(module):
|
|
239
|
+
return None
|
|
240
|
+
|
|
50
241
|
function_name = frame.f_code.co_name
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
242
|
+
|
|
243
|
+
# Handle different event types
|
|
244
|
+
if event == 'call':
|
|
245
|
+
self.current_depth += 1
|
|
246
|
+
|
|
247
|
+
# Check breakpoints
|
|
248
|
+
should_break = False
|
|
249
|
+
for bp in self.breakpoints.values():
|
|
250
|
+
if bp.matches(module, function_name):
|
|
251
|
+
if self._check_condition(bp, frame):
|
|
252
|
+
bp.hit_count += 1
|
|
253
|
+
self.logger.info(f"Breakpoint hit at {module}.{function_name} (hit #{bp.hit_count})")
|
|
254
|
+
should_break = True
|
|
255
|
+
break
|
|
256
|
+
|
|
257
|
+
if should_break:
|
|
60
258
|
self.paused = True
|
|
259
|
+
self.current_frame = frame
|
|
260
|
+
self.frame_stack = self._build_frame_stack(frame)
|
|
261
|
+
self.current_frame_index = 0
|
|
61
262
|
self.handle_breakpoint(frame)
|
|
62
263
|
self.paused = False
|
|
264
|
+
|
|
265
|
+
elif event == 'return':
|
|
266
|
+
self.current_depth -= 1
|
|
267
|
+
|
|
268
|
+
# Handle step-out (return from function)
|
|
269
|
+
if self.step_mode == 'return' and frame == self.step_frame:
|
|
270
|
+
self.step_mode = None
|
|
271
|
+
self.paused = True
|
|
272
|
+
self.current_frame = frame
|
|
273
|
+
self.frame_stack = self._build_frame_stack(frame)
|
|
274
|
+
self.current_frame_index = 0
|
|
275
|
+
self.handle_breakpoint(frame)
|
|
276
|
+
self.paused = False
|
|
277
|
+
|
|
278
|
+
elif event == 'line':
|
|
279
|
+
# Handle step and next
|
|
280
|
+
if self.step_mode == 'step':
|
|
281
|
+
self.step_mode = None
|
|
282
|
+
self.paused = True
|
|
283
|
+
self.current_frame = frame
|
|
284
|
+
self.frame_stack = self._build_frame_stack(frame)
|
|
285
|
+
self.current_frame_index = 0
|
|
286
|
+
self.handle_breakpoint(frame)
|
|
287
|
+
self.paused = False
|
|
288
|
+
elif self.step_mode == 'next' and self.current_depth <= self.step_depth:
|
|
289
|
+
self.step_mode = None
|
|
290
|
+
self.paused = True
|
|
291
|
+
self.current_frame = frame
|
|
292
|
+
self.frame_stack = self._build_frame_stack(frame)
|
|
293
|
+
self.current_frame_index = 0
|
|
294
|
+
self.handle_breakpoint(frame)
|
|
295
|
+
self.paused = False
|
|
296
|
+
|
|
63
297
|
return self.trace_function
|
|
64
298
|
|
|
65
|
-
def
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
|
|
299
|
+
def _get_source_lines(self, frame: FrameType, context: int = 5) -> Optional[List[Tuple[int, str]]]:
|
|
300
|
+
"""Get source code lines around the current line."""
|
|
301
|
+
try:
|
|
302
|
+
filename = frame.f_code.co_filename
|
|
303
|
+
lineno = frame.f_lineno
|
|
304
|
+
|
|
305
|
+
with open(filename, 'r') as f:
|
|
306
|
+
lines = f.readlines()
|
|
307
|
+
|
|
308
|
+
start = max(0, lineno - context - 1)
|
|
309
|
+
end = min(len(lines), lineno + context)
|
|
310
|
+
|
|
311
|
+
return [(i + 1, lines[i].rstrip()) for i in range(start, end)]
|
|
312
|
+
except Exception as e:
|
|
313
|
+
self.logger.debug(f"Could not get source: {e}")
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
def _print_source(self, frame: FrameType, context: int = 5):
|
|
317
|
+
"""Print source code around current line."""
|
|
318
|
+
lines = self._get_source_lines(frame, context)
|
|
319
|
+
if not lines:
|
|
320
|
+
Console.write_stdout("Source code not available.")
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
lineno = frame.f_lineno
|
|
324
|
+
Console.write_stdout(f"\nSource ({frame.f_code.co_filename}):")
|
|
325
|
+
for num, line in lines:
|
|
326
|
+
marker = "-> " if num == lineno else " "
|
|
327
|
+
Console.write_stdout(f"{marker}{num:4d} {line}")
|
|
69
328
|
|
|
70
|
-
|
|
71
|
-
|
|
329
|
+
def _print_stack_trace(self):
|
|
330
|
+
"""Print stack trace."""
|
|
331
|
+
Console.write_stdout("\nStack trace (most recent call last):")
|
|
332
|
+
for i, frame in enumerate(reversed(self.frame_stack)):
|
|
333
|
+
marker = ">" if i == len(self.frame_stack) - 1 - self.current_frame_index else " "
|
|
334
|
+
module = frame.f_globals.get('__name__', '?')
|
|
335
|
+
func = frame.f_code.co_name
|
|
336
|
+
lineno = frame.f_lineno
|
|
337
|
+
filename = frame.f_code.co_filename
|
|
338
|
+
Console.write_stdout(f" {marker} #{i:2d} {module}.{func} at {filename}:{lineno}")
|
|
339
|
+
|
|
340
|
+
def _move_frame(self, direction: int) -> bool:
|
|
341
|
+
"""Move up or down the frame stack."""
|
|
342
|
+
new_index = self.current_frame_index + direction
|
|
343
|
+
if 0 <= new_index < len(self.frame_stack):
|
|
344
|
+
self.current_frame_index = new_index
|
|
345
|
+
self.current_frame = self.frame_stack[new_index]
|
|
346
|
+
Console.write_stdout(f"Frame #{len(self.frame_stack) - 1 - new_index}: "
|
|
347
|
+
f"{self.current_frame.f_globals.get('__name__', '?')}."
|
|
348
|
+
f"{self.current_frame.f_code.co_name}")
|
|
349
|
+
return True
|
|
350
|
+
else:
|
|
351
|
+
Console.write_stdout(f"Cannot move {direction} (at {'top' if direction < 0 else 'bottom'} of stack)")
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
def handle_breakpoint(self, frame: FrameType):
|
|
355
|
+
"""Handle a breakpoint by prompting for interactive commands."""
|
|
356
|
+
module = frame.f_globals.get('__name__')
|
|
357
|
+
func = frame.f_code.co_name
|
|
358
|
+
|
|
359
|
+
Console.write_stdout(f"\n{'='*60}")
|
|
360
|
+
Console.write_stdout(f"Paused at {module}.{func} (line {frame.f_lineno})")
|
|
361
|
+
self._print_source(frame, context=3)
|
|
362
|
+
Console.write_stdout(f"{'='*60}")
|
|
363
|
+
Console.write_stdout("Type 'h' for help, 'c' to continue")
|
|
364
|
+
|
|
72
365
|
while True:
|
|
73
366
|
try:
|
|
74
|
-
command = input("(geai-dbg) ")
|
|
75
|
-
if
|
|
367
|
+
command = input("(geai-dbg) ").strip()
|
|
368
|
+
if not command:
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
parts = command.split(None, 1)
|
|
372
|
+
cmd = parts[0]
|
|
373
|
+
args = parts[1] if len(parts) > 1 else ""
|
|
374
|
+
|
|
375
|
+
# Control flow commands
|
|
376
|
+
if cmd in ('continue', 'c'):
|
|
76
377
|
break
|
|
77
|
-
elif
|
|
78
|
-
|
|
378
|
+
elif cmd in ('quit', 'q'):
|
|
379
|
+
self.logger.info("Debugger terminated by user.")
|
|
79
380
|
sys.exit(0)
|
|
80
|
-
elif
|
|
81
|
-
|
|
82
|
-
sys.settrace(None)
|
|
381
|
+
elif cmd in ('run', 'r'):
|
|
382
|
+
self.logger.info("Running program without further pauses.")
|
|
383
|
+
sys.settrace(None)
|
|
384
|
+
break
|
|
385
|
+
|
|
386
|
+
# Stepping commands
|
|
387
|
+
elif cmd in ('step', 's'):
|
|
388
|
+
self.step_mode = 'step'
|
|
389
|
+
self.step_depth = self.current_depth
|
|
83
390
|
break
|
|
84
|
-
elif
|
|
85
|
-
|
|
391
|
+
elif cmd in ('next', 'n'):
|
|
392
|
+
self.step_mode = 'next'
|
|
393
|
+
self.step_depth = self.current_depth
|
|
394
|
+
break
|
|
395
|
+
elif cmd in ('return', 'ret'):
|
|
396
|
+
self.step_mode = 'return'
|
|
397
|
+
self.step_frame = frame
|
|
398
|
+
break
|
|
399
|
+
|
|
400
|
+
# Stack navigation
|
|
401
|
+
elif cmd in ('up', 'u'):
|
|
402
|
+
self._move_frame(1)
|
|
403
|
+
elif cmd in ('down', 'd'):
|
|
404
|
+
self._move_frame(-1)
|
|
405
|
+
elif cmd in ('where', 'w', 'bt', 'backtrace'):
|
|
406
|
+
self._print_stack_trace()
|
|
407
|
+
|
|
408
|
+
# Source display
|
|
409
|
+
elif cmd in ('list', 'l'):
|
|
410
|
+
context = int(args) if args.isdigit() else 10
|
|
411
|
+
self._print_source(self.current_frame, context)
|
|
412
|
+
|
|
413
|
+
# Variable inspection
|
|
414
|
+
elif cmd in ('print', 'p'):
|
|
415
|
+
if not args:
|
|
416
|
+
Console.write_stdout("Usage: p <expression>")
|
|
417
|
+
else:
|
|
418
|
+
try:
|
|
419
|
+
result = eval(args, self.current_frame.f_globals, self.current_frame.f_locals)
|
|
420
|
+
Console.write_stdout(repr(result))
|
|
421
|
+
except Exception as e:
|
|
422
|
+
Console.write_stdout(f"Error: {e}")
|
|
423
|
+
|
|
424
|
+
elif cmd in ('pp',):
|
|
425
|
+
if not args:
|
|
426
|
+
Console.write_stdout("Usage: pp <expression>")
|
|
427
|
+
else:
|
|
428
|
+
try:
|
|
429
|
+
result = eval(args, self.current_frame.f_globals, self.current_frame.f_locals)
|
|
430
|
+
pprint.pprint(result)
|
|
431
|
+
except Exception as e:
|
|
432
|
+
Console.write_stdout(f"Error: {e}")
|
|
433
|
+
|
|
434
|
+
elif cmd in ('locals', 'loc'):
|
|
435
|
+
Console.write_stdout("\nLocal variables:")
|
|
436
|
+
pprint.pprint(dict(self.current_frame.f_locals))
|
|
437
|
+
|
|
438
|
+
elif cmd in ('globals', 'glob'):
|
|
439
|
+
Console.write_stdout("\nGlobal variables:")
|
|
440
|
+
# Filter out builtins
|
|
441
|
+
filtered = {k: v for k, v in self.current_frame.f_globals.items()
|
|
442
|
+
if not k.startswith('__')}
|
|
443
|
+
pprint.pprint(filtered)
|
|
444
|
+
|
|
445
|
+
elif cmd in ('args', 'a'):
|
|
446
|
+
Console.write_stdout("\nFunction arguments:")
|
|
447
|
+
arginfo = inspect.getargvalues(self.current_frame)
|
|
448
|
+
for arg in arginfo.args:
|
|
449
|
+
Console.write_stdout(f" {arg} = {repr(self.current_frame.f_locals.get(arg))}")
|
|
450
|
+
|
|
451
|
+
# Breakpoint management
|
|
452
|
+
elif cmd in ('break', 'b'):
|
|
453
|
+
if not args:
|
|
454
|
+
# List breakpoints
|
|
455
|
+
bps = self.list_breakpoints()
|
|
456
|
+
if bps:
|
|
457
|
+
Console.write_stdout("\nBreakpoints:")
|
|
458
|
+
for i, bp in enumerate(bps, 1):
|
|
459
|
+
Console.write_stdout(f" {i}. {bp}")
|
|
460
|
+
else:
|
|
461
|
+
Console.write_stdout("No breakpoints set.")
|
|
462
|
+
else:
|
|
463
|
+
# Parse breakpoint specification: module:function or just function
|
|
464
|
+
if ':' in args:
|
|
465
|
+
mod, func = args.split(':', 1)
|
|
466
|
+
mod = mod.strip() or None
|
|
467
|
+
func = func.strip() or None
|
|
468
|
+
else:
|
|
469
|
+
mod = None
|
|
470
|
+
func = args.strip()
|
|
471
|
+
self.add_breakpoint(module=mod, function_name=func)
|
|
472
|
+
|
|
473
|
+
elif cmd in ('tbreak', 'tb'):
|
|
474
|
+
Console.write_stdout("Temporary breakpoints not yet implemented.")
|
|
475
|
+
|
|
476
|
+
elif cmd in ('clear', 'cl'):
|
|
477
|
+
if args:
|
|
478
|
+
if ':' in args:
|
|
479
|
+
mod, func = args.split(':', 1)
|
|
480
|
+
mod = mod.strip() or None
|
|
481
|
+
func = func.strip() or None
|
|
482
|
+
else:
|
|
483
|
+
mod = None
|
|
484
|
+
func = args.strip()
|
|
485
|
+
self.remove_breakpoint(module=mod, function_name=func)
|
|
486
|
+
else:
|
|
487
|
+
Console.write_stdout("Usage: cl <breakpoint> or 'clearall' to remove all")
|
|
488
|
+
|
|
489
|
+
elif cmd in ('clearall', 'cla'):
|
|
490
|
+
self.clear_breakpoints()
|
|
491
|
+
|
|
492
|
+
elif cmd in ('enable', 'en'):
|
|
493
|
+
if ':' in args:
|
|
494
|
+
mod, func = args.split(':', 1)
|
|
495
|
+
mod = mod.strip() or None
|
|
496
|
+
func = func.strip() or None
|
|
497
|
+
else:
|
|
498
|
+
mod = None
|
|
499
|
+
func = args.strip()
|
|
500
|
+
self.enable_breakpoint(module=mod, function_name=func)
|
|
501
|
+
|
|
502
|
+
elif cmd in ('disable', 'dis'):
|
|
503
|
+
if ':' in args:
|
|
504
|
+
mod, func = args.split(':', 1)
|
|
505
|
+
mod = mod.strip() or None
|
|
506
|
+
func = func.strip() or None
|
|
507
|
+
else:
|
|
508
|
+
mod = None
|
|
509
|
+
func = args.strip()
|
|
510
|
+
self.disable_breakpoint(module=mod, function_name=func)
|
|
511
|
+
|
|
512
|
+
# Legacy commands
|
|
513
|
+
elif cmd in ('breakpoint-module', 'bm'):
|
|
514
|
+
self.logger.info("Adding breakpoint on module")
|
|
86
515
|
module_name = input("(geai-dbg) Enter module name (or press Enter for any module): ").strip()
|
|
87
516
|
module_name = module_name if module_name else None
|
|
88
517
|
self.add_breakpoint(module=module_name)
|
|
89
|
-
|
|
90
|
-
|
|
518
|
+
|
|
519
|
+
elif cmd in ('breakpoint-function', 'bf'):
|
|
520
|
+
self.logger.info("Adding breakpoint on function name")
|
|
91
521
|
function_name = input("(geai-dbg) Enter function name (or press Enter for any function): ").strip()
|
|
92
522
|
function_name = function_name if function_name else None
|
|
93
523
|
module_name = input("(geai-dbg) Enter module name (optional, press Enter to skip): ").strip()
|
|
94
524
|
module_name = module_name if module_name else None
|
|
95
525
|
self.add_breakpoint(module=module_name, function_name=function_name)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
526
|
+
|
|
527
|
+
elif cmd in ('list-modules', 'lm'):
|
|
528
|
+
self.logger.info("Listing available modules")
|
|
529
|
+
modules = [m for m in sys.modules if m.startswith(self.module_filter)]
|
|
530
|
+
Console.write_stdout(f"\nAvailable modules ({len(modules)}):")
|
|
531
|
+
for mod in sorted(modules)[:50]: # Limit output
|
|
532
|
+
Console.write_stdout(f" {mod}")
|
|
533
|
+
if len(modules) > 50:
|
|
534
|
+
Console.write_stdout(f" ... and {len(modules) - 50} more")
|
|
535
|
+
|
|
536
|
+
# Help
|
|
537
|
+
elif cmd in ('help', 'h', '?'):
|
|
538
|
+
self._print_help()
|
|
539
|
+
|
|
540
|
+
# Execute arbitrary Python code
|
|
109
541
|
else:
|
|
110
|
-
|
|
542
|
+
self.logger.info(f"Executing interactive command: {command}")
|
|
111
543
|
try:
|
|
112
|
-
exec
|
|
544
|
+
# Try exec first, if it fails try eval and print result
|
|
545
|
+
try:
|
|
546
|
+
exec(command, self.current_frame.f_globals, self.current_frame.f_locals)
|
|
547
|
+
except SyntaxError:
|
|
548
|
+
result = eval(command, self.current_frame.f_globals, self.current_frame.f_locals)
|
|
549
|
+
Console.write_stdout(repr(result))
|
|
113
550
|
except Exception as e:
|
|
114
|
-
|
|
551
|
+
self.logger.error(f"Command execution failed: {e}")
|
|
552
|
+
Console.write_stdout(f"Error: {e}")
|
|
553
|
+
|
|
115
554
|
except EOFError:
|
|
116
|
-
|
|
555
|
+
self.logger.info("Debugger terminated by user (EOF).")
|
|
117
556
|
sys.exit(0)
|
|
118
557
|
except KeyboardInterrupt:
|
|
119
|
-
|
|
558
|
+
self.logger.info("Keyboard interrupt received. Continuing execution.")
|
|
120
559
|
break
|
|
121
560
|
|
|
561
|
+
def _print_help(self):
|
|
562
|
+
"""Print help message."""
|
|
563
|
+
help_text = """
|
|
564
|
+
Available commands:
|
|
565
|
+
|
|
566
|
+
Flow Control:
|
|
567
|
+
continue, c Resume execution until next breakpoint
|
|
568
|
+
step, s Step into function calls
|
|
569
|
+
next, n Step over function calls (same level)
|
|
570
|
+
return, ret Continue until current function returns
|
|
571
|
+
run, r Run program to completion (disable tracing)
|
|
572
|
+
quit, q Exit the debugger
|
|
573
|
+
|
|
574
|
+
Stack Navigation:
|
|
575
|
+
where, w, bt Show stack trace
|
|
576
|
+
up, u Move up one stack frame
|
|
577
|
+
down, d Move down one stack frame
|
|
578
|
+
|
|
579
|
+
Source Display:
|
|
580
|
+
list, l [n] Show source code (n lines of context, default 10)
|
|
581
|
+
|
|
582
|
+
Variable Inspection:
|
|
583
|
+
print, p <expr> Evaluate and print expression
|
|
584
|
+
pp <expr> Pretty-print expression
|
|
585
|
+
locals, loc Show local variables
|
|
586
|
+
globals, glob Show global variables
|
|
587
|
+
args, a Show function arguments
|
|
588
|
+
|
|
589
|
+
Breakpoints:
|
|
590
|
+
break, b List all breakpoints
|
|
591
|
+
b <func> Set breakpoint on function
|
|
592
|
+
b <module>:<func> Set breakpoint on module:function
|
|
593
|
+
clear, cl <bp> Remove breakpoint
|
|
594
|
+
clearall, cla Remove all breakpoints
|
|
595
|
+
enable, en <bp> Enable breakpoint
|
|
596
|
+
disable, dis <bp> Disable breakpoint
|
|
597
|
+
|
|
598
|
+
Legacy Commands:
|
|
599
|
+
breakpoint-module, bm Add module breakpoint (interactive)
|
|
600
|
+
breakpoint-function, bf Add function breakpoint (interactive)
|
|
601
|
+
list-modules, lm List available modules
|
|
602
|
+
|
|
603
|
+
Other:
|
|
604
|
+
help, h, ? Show this help
|
|
605
|
+
<Python code> Execute arbitrary Python code
|
|
606
|
+
|
|
607
|
+
Examples:
|
|
608
|
+
p sys.argv Print command-line arguments
|
|
609
|
+
b main Set breakpoint on any 'main' function
|
|
610
|
+
b pygeai.cli:main Set breakpoint on pygeai.cli.main
|
|
611
|
+
pp locals() Pretty-print all local variables
|
|
612
|
+
"""
|
|
613
|
+
Console.write_stdout(help_text)
|
|
614
|
+
|
|
122
615
|
def run(self):
|
|
123
|
-
|
|
616
|
+
"""Run the target callable under the debugger."""
|
|
617
|
+
self.logger.info("Setting trace and running target")
|
|
124
618
|
sys.settrace(self.trace_function)
|
|
125
619
|
try:
|
|
126
|
-
|
|
620
|
+
self.target()
|
|
127
621
|
except Exception as e:
|
|
128
|
-
|
|
622
|
+
self.logger.error(f"Target execution failed: {e}")
|
|
129
623
|
raise
|
|
130
624
|
finally:
|
|
131
|
-
|
|
625
|
+
self.logger.info("Cleaning up trace")
|
|
132
626
|
sys.settrace(None)
|
|
133
627
|
|
|
134
628
|
|
|
135
629
|
def main():
|
|
630
|
+
"""Entry point for geai-dbg command."""
|
|
136
631
|
dbg = Debugger()
|
|
137
632
|
dbg.add_breakpoint(module='pygeai.cli.geai', function_name='main')
|
|
138
633
|
dbg.run()
|