praisonaiagents 0.0.23__py3-none-any.whl → 0.0.24__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,423 @@
1
+ """Tools for Python code execution and analysis.
2
+
3
+ Usage:
4
+ from praisonaiagents.tools import python_tools
5
+ result = python_tools.execute_code("print('Hello, World!')")
6
+
7
+ or
8
+ from praisonaiagents.tools import execute_code, analyze_code, format_code
9
+ result = execute_code("print('Hello, World!')")
10
+ """
11
+
12
+ import logging
13
+ from typing import Dict, List, Optional, Any
14
+ from importlib import util
15
+ import io
16
+ from contextlib import redirect_stdout, redirect_stderr
17
+ import traceback
18
+
19
+ class PythonTools:
20
+ """Tools for Python code execution and analysis."""
21
+
22
+ def __init__(self):
23
+ """Initialize PythonTools."""
24
+ self._check_dependencies()
25
+
26
+ def _check_dependencies(self):
27
+ """Check if required packages are installed."""
28
+ missing = []
29
+ for package in ['black', 'pylint', 'autopep8']:
30
+ if util.find_spec(package) is None:
31
+ missing.append(package)
32
+
33
+ if missing:
34
+ raise ImportError(
35
+ f"Required packages not available. Please install: {', '.join(missing)}\n"
36
+ f"Run: pip install {' '.join(missing)}"
37
+ )
38
+
39
+ def execute_code(
40
+ self,
41
+ code: str,
42
+ globals_dict: Optional[Dict[str, Any]] = None,
43
+ locals_dict: Optional[Dict[str, Any]] = None,
44
+ timeout: int = 30,
45
+ max_output_size: int = 10000
46
+ ) -> Dict[str, Any]:
47
+ """Execute Python code safely."""
48
+ try:
49
+ # Set up execution environment
50
+ if globals_dict is None:
51
+ globals_dict = {'__builtins__': __builtins__}
52
+ if locals_dict is None:
53
+ locals_dict = {}
54
+
55
+ # Capture output
56
+ stdout_buffer = io.StringIO()
57
+ stderr_buffer = io.StringIO()
58
+
59
+ try:
60
+ # Compile code
61
+ compiled_code = compile(code, '<string>', 'exec')
62
+
63
+ # Execute with output capture
64
+ with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer):
65
+ exec(compiled_code, globals_dict, locals_dict)
66
+
67
+ # Get last expression value if any
68
+ import ast
69
+ tree = ast.parse(code)
70
+ if tree.body and isinstance(tree.body[-1], ast.Expr):
71
+ result = eval(
72
+ compile(ast.Expression(tree.body[-1].value), '<string>', 'eval'),
73
+ globals_dict,
74
+ locals_dict
75
+ )
76
+ else:
77
+ result = None
78
+
79
+ # Get output
80
+ stdout = stdout_buffer.getvalue()
81
+ stderr = stderr_buffer.getvalue()
82
+
83
+ # Truncate output if too large
84
+ if len(stdout) > max_output_size:
85
+ stdout = stdout[:max_output_size] + "...[truncated]"
86
+ if len(stderr) > max_output_size:
87
+ stderr = stderr[:max_output_size] + "...[truncated]"
88
+
89
+ return {
90
+ 'result': result,
91
+ 'stdout': stdout,
92
+ 'stderr': stderr,
93
+ 'success': True
94
+ }
95
+
96
+ except Exception as e:
97
+ error_msg = f"Error executing code: {str(e)}"
98
+ logging.error(error_msg)
99
+ return {
100
+ 'result': None,
101
+ 'stdout': stdout_buffer.getvalue(),
102
+ 'stderr': error_msg,
103
+ 'success': False
104
+ }
105
+
106
+ except Exception as e:
107
+ error_msg = f"Error executing code: {str(e)}"
108
+ logging.error(error_msg)
109
+ return {
110
+ 'result': None,
111
+ 'stdout': '',
112
+ 'stderr': error_msg,
113
+ 'success': False
114
+ }
115
+
116
+ def analyze_code(
117
+ self,
118
+ code: str
119
+ ) -> Optional[Dict[str, Any]]:
120
+ """Analyze Python code structure and quality.
121
+
122
+ Args:
123
+ code: Python code to analyze
124
+
125
+ Returns:
126
+ Dictionary with analysis results
127
+ """
128
+ try:
129
+ # Import ast only when needed
130
+ import ast
131
+
132
+ # Parse code
133
+ tree = ast.parse(code)
134
+
135
+ # Analyze structure
136
+ analysis = {
137
+ 'imports': [],
138
+ 'functions': [],
139
+ 'classes': [],
140
+ 'variables': [],
141
+ 'complexity': {
142
+ 'lines': len(code.splitlines()),
143
+ 'functions': 0,
144
+ 'classes': 0,
145
+ 'branches': 0
146
+ }
147
+ }
148
+
149
+ # Analyze nodes
150
+ for node in ast.walk(tree):
151
+ if isinstance(node, ast.Import):
152
+ for name in node.names:
153
+ analysis['imports'].append(name.name)
154
+ elif isinstance(node, ast.ImportFrom):
155
+ module = node.module or ''
156
+ for name in node.names:
157
+ analysis['imports'].append(f"{module}.{name.name}")
158
+ elif isinstance(node, ast.FunctionDef):
159
+ analysis['functions'].append({
160
+ 'name': node.name,
161
+ 'args': [
162
+ arg.arg for arg in node.args.args
163
+ ],
164
+ 'decorators': [
165
+ ast.unparse(d) for d in node.decorator_list
166
+ ]
167
+ })
168
+ analysis['complexity']['functions'] += 1
169
+ elif isinstance(node, ast.ClassDef):
170
+ analysis['classes'].append({
171
+ 'name': node.name,
172
+ 'bases': [
173
+ ast.unparse(base) for base in node.bases
174
+ ],
175
+ 'decorators': [
176
+ ast.unparse(d) for d in node.decorator_list
177
+ ]
178
+ })
179
+ analysis['complexity']['classes'] += 1
180
+ elif isinstance(node, ast.Assign):
181
+ for target in node.targets:
182
+ if isinstance(target, ast.Name):
183
+ analysis['variables'].append(target.id)
184
+ elif isinstance(node, (ast.If, ast.While, ast.For)):
185
+ analysis['complexity']['branches'] += 1
186
+
187
+ return analysis
188
+ except Exception as e:
189
+ error_msg = f"Error analyzing code: {str(e)}"
190
+ logging.error(error_msg)
191
+ return None
192
+
193
+ def format_code(
194
+ self,
195
+ code: str,
196
+ style: str = 'black',
197
+ line_length: int = 88
198
+ ) -> Optional[str]:
199
+ """Format Python code according to style guide.
200
+
201
+ Args:
202
+ code: Python code to format
203
+ style: Formatting style ('black' or 'pep8')
204
+ line_length: Maximum line length
205
+
206
+ Returns:
207
+ Formatted code
208
+ """
209
+ try:
210
+ if util.find_spec(style) is None:
211
+ error_msg = f"{style} package is not available. Please install it using: pip install {style}"
212
+ logging.error(error_msg)
213
+ return None
214
+
215
+ if style == 'black':
216
+ import black
217
+ return black.format_str(
218
+ code,
219
+ mode=black.FileMode(
220
+ line_length=line_length
221
+ )
222
+ )
223
+ else: # pep8
224
+ import autopep8
225
+ return autopep8.fix_code(
226
+ code,
227
+ options={
228
+ 'max_line_length': line_length
229
+ }
230
+ )
231
+ except Exception as e:
232
+ error_msg = f"Error formatting code: {str(e)}"
233
+ logging.error(error_msg)
234
+ return None
235
+
236
+ def lint_code(
237
+ self,
238
+ code: str
239
+ ) -> Optional[Dict[str, List[Dict[str, Any]]]]:
240
+ """Lint Python code for potential issues.
241
+
242
+ Args:
243
+ code: Python code to lint
244
+
245
+ Returns:
246
+ Dictionary with linting results
247
+ """
248
+ try:
249
+ if util.find_spec('pylint') is None:
250
+ error_msg = "pylint package is not available. Please install it using: pip install pylint"
251
+ logging.error(error_msg)
252
+ return None
253
+
254
+ # Import pylint only when needed
255
+ from pylint.reporters import JSONReporter
256
+ from pylint.lint.run import Run
257
+
258
+ # Create temporary file for pylint
259
+ import tempfile
260
+ with tempfile.NamedTemporaryFile(
261
+ mode='w',
262
+ suffix='.py',
263
+ delete=False
264
+ ) as f:
265
+ f.write(code)
266
+ temp_path = f.name
267
+
268
+ # Run pylint
269
+ reporter = JSONReporter()
270
+ Run(
271
+ [temp_path],
272
+ reporter=reporter,
273
+ exit=False
274
+ )
275
+
276
+ # Process results
277
+ results = {
278
+ 'errors': [],
279
+ 'warnings': [],
280
+ 'conventions': []
281
+ }
282
+
283
+ for msg in reporter.messages:
284
+ item = {
285
+ 'type': msg.category,
286
+ 'module': msg.module,
287
+ 'obj': msg.obj,
288
+ 'line': msg.line,
289
+ 'column': msg.column,
290
+ 'path': msg.path,
291
+ 'symbol': msg.symbol,
292
+ 'message': msg.msg,
293
+ 'message-id': msg.msg_id
294
+ }
295
+
296
+ if msg.category in ['error', 'fatal']:
297
+ results['errors'].append(item)
298
+ elif msg.category == 'warning':
299
+ results['warnings'].append(item)
300
+ else:
301
+ results['conventions'].append(item)
302
+
303
+ # Clean up
304
+ import os
305
+ os.unlink(temp_path)
306
+
307
+ return results
308
+ except Exception as e:
309
+ error_msg = f"Error linting code: {str(e)}"
310
+ logging.error(error_msg)
311
+ return {
312
+ 'errors': [],
313
+ 'warnings': [],
314
+ 'conventions': []
315
+ }
316
+
317
+ def disassemble_code(
318
+ self,
319
+ code: str
320
+ ) -> Optional[str]:
321
+ """Disassemble Python code to bytecode.
322
+
323
+ Args:
324
+ code: Python code to disassemble
325
+
326
+ Returns:
327
+ Disassembled bytecode as string
328
+ """
329
+ try:
330
+ # Import dis only when needed
331
+ import dis
332
+
333
+ # Compile code
334
+ compiled_code = compile(code, '<string>', 'exec')
335
+
336
+ # Capture disassembly
337
+ output = io.StringIO()
338
+ with redirect_stdout(output):
339
+ dis.dis(compiled_code)
340
+
341
+ return output.getvalue()
342
+ except Exception as e:
343
+ error_msg = f"Error disassembling code: {str(e)}"
344
+ logging.error(error_msg)
345
+ return None
346
+
347
+ # Create instance for direct function access
348
+ _python_tools = PythonTools()
349
+ execute_code = _python_tools.execute_code
350
+ analyze_code = _python_tools.analyze_code
351
+ format_code = _python_tools.format_code
352
+ lint_code = _python_tools.lint_code
353
+ disassemble_code = _python_tools.disassemble_code
354
+
355
+ if __name__ == "__main__":
356
+ print("\n==================================================")
357
+ print("PythonTools Demonstration")
358
+ print("==================================================\n")
359
+
360
+ print("1. Execute Python Code")
361
+ print("------------------------------")
362
+ code = """
363
+ def greet(name):
364
+ return f"Hello, {name}!"
365
+
366
+ result = greet("World")
367
+ print(result)
368
+ """
369
+ print("Code to execute:")
370
+ print(code)
371
+ print("\nOutput:")
372
+ result = execute_code(code)
373
+ print(result["stdout"])
374
+ print()
375
+
376
+ print("2. Format Python Code")
377
+ print("------------------------------")
378
+ code = """
379
+ def messy_function(x,y, z):
380
+ if x>0:
381
+ return y+z
382
+ else:
383
+ return y-z
384
+ """
385
+ print("Before formatting:")
386
+ print(code)
387
+ print("\nAfter formatting:")
388
+ result = format_code(code)
389
+ print(result)
390
+ print()
391
+
392
+ print("3. Lint Python Code")
393
+ print("------------------------------")
394
+ code = """
395
+ def bad_function():
396
+ unused_var = 42
397
+ return 'result'
398
+ """
399
+ print("Code to lint:")
400
+ print(code)
401
+ print("\nLinting results:")
402
+ result = lint_code(code)
403
+ print(result)
404
+ print()
405
+
406
+ print("4. Execute Code with Variables")
407
+ print("------------------------------")
408
+ code = """
409
+ x = 10
410
+ y = 20
411
+ result = x + y
412
+ print(f"The sum of {x} and {y} is {result}")
413
+ """
414
+ print("Code to execute:")
415
+ print(code)
416
+ print("\nOutput:")
417
+ result = execute_code(code)
418
+ print(result["stdout"])
419
+ print()
420
+
421
+ print("==================================================")
422
+ print("Demonstration Complete")
423
+ print("==================================================")
@@ -0,0 +1,278 @@
1
+ """Tools for executing shell commands safely.
2
+
3
+ This module provides a safe interface for executing shell commands with:
4
+ - Timeout control
5
+ - Output capture
6
+ - Error handling
7
+ - Resource limits
8
+ """
9
+
10
+ import subprocess
11
+ import shlex
12
+ import logging
13
+ import os
14
+ import time
15
+ from typing import Dict, List, Optional, Union
16
+
17
+ class ShellTools:
18
+ """Tools for executing shell commands safely."""
19
+
20
+ def __init__(self):
21
+ """Initialize ShellTools."""
22
+ self._check_dependencies()
23
+
24
+ def _check_dependencies(self):
25
+ """Check if required packages are installed."""
26
+ try:
27
+ import psutil
28
+ except ImportError:
29
+ raise ImportError(
30
+ "Required package not available. Please install: psutil\n"
31
+ "Run: pip install psutil"
32
+ )
33
+
34
+ def execute_command(
35
+ self,
36
+ command: str,
37
+ cwd: Optional[str] = None,
38
+ timeout: int = 30,
39
+ shell: bool = False,
40
+ env: Optional[Dict[str, str]] = None,
41
+ max_output_size: int = 10000
42
+ ) -> Dict[str, Union[str, int, bool]]:
43
+ """Execute a shell command safely.
44
+
45
+ Args:
46
+ command: Command to execute
47
+ cwd: Working directory
48
+ timeout: Maximum execution time in seconds
49
+ shell: Whether to run command in shell
50
+ env: Environment variables
51
+ max_output_size: Maximum output size in bytes
52
+
53
+ Returns:
54
+ Dictionary with execution results
55
+ """
56
+ try:
57
+ # Split command if not using shell
58
+ if not shell:
59
+ command = shlex.split(command)
60
+
61
+ # Set up process environment
62
+ process_env = os.environ.copy()
63
+ if env:
64
+ process_env.update(env)
65
+
66
+ # Start process
67
+ start_time = time.time()
68
+ process = subprocess.Popen(
69
+ command,
70
+ stdout=subprocess.PIPE,
71
+ stderr=subprocess.PIPE,
72
+ cwd=cwd,
73
+ shell=shell,
74
+ env=process_env,
75
+ text=True
76
+ )
77
+
78
+ try:
79
+ # Wait for process with timeout
80
+ stdout, stderr = process.communicate(timeout=timeout)
81
+
82
+ # Truncate output if too large
83
+ if len(stdout) > max_output_size:
84
+ stdout = stdout[:max_output_size] + "...[truncated]"
85
+ if len(stderr) > max_output_size:
86
+ stderr = stderr[:max_output_size] + "...[truncated]"
87
+
88
+ return {
89
+ 'stdout': stdout,
90
+ 'stderr': stderr,
91
+ 'exit_code': process.returncode,
92
+ 'success': process.returncode == 0,
93
+ 'execution_time': time.time() - start_time
94
+ }
95
+
96
+ except subprocess.TimeoutExpired:
97
+ # Kill process on timeout
98
+ parent = psutil.Process(process.pid)
99
+ children = parent.children(recursive=True)
100
+ for child in children:
101
+ child.kill()
102
+ parent.kill()
103
+
104
+ return {
105
+ 'stdout': '',
106
+ 'stderr': f'Command timed out after {timeout} seconds',
107
+ 'exit_code': -1,
108
+ 'success': False,
109
+ 'execution_time': timeout
110
+ }
111
+
112
+ except Exception as e:
113
+ error_msg = f"Error executing command: {str(e)}"
114
+ logging.error(error_msg)
115
+ return {
116
+ 'stdout': '',
117
+ 'stderr': error_msg,
118
+ 'exit_code': -1,
119
+ 'success': False,
120
+ 'execution_time': 0
121
+ }
122
+
123
+ def list_processes(self) -> List[Dict[str, Union[int, str, float]]]:
124
+ """List running processes with their details.
125
+
126
+ Returns:
127
+ List of process information dictionaries
128
+ """
129
+ try:
130
+ processes = []
131
+ for proc in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', 'cpu_percent']):
132
+ try:
133
+ pinfo = proc.info
134
+ processes.append({
135
+ 'pid': pinfo['pid'],
136
+ 'name': pinfo['name'],
137
+ 'username': pinfo['username'],
138
+ 'memory_percent': round(pinfo['memory_percent'], 2),
139
+ 'cpu_percent': round(pinfo['cpu_percent'], 2)
140
+ })
141
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
142
+ pass
143
+ return processes
144
+ except Exception as e:
145
+ error_msg = f"Error listing processes: {str(e)}"
146
+ logging.error(error_msg)
147
+ return []
148
+
149
+ def kill_process(
150
+ self,
151
+ pid: int,
152
+ force: bool = False
153
+ ) -> Dict[str, Union[bool, str]]:
154
+ """Kill a process by its PID.
155
+
156
+ Args:
157
+ pid: Process ID to kill
158
+ force: Whether to force kill (-9)
159
+
160
+ Returns:
161
+ Dictionary with operation results
162
+ """
163
+ try:
164
+ process = psutil.Process(pid)
165
+ if force:
166
+ process.kill() # SIGKILL
167
+ else:
168
+ process.terminate() # SIGTERM
169
+
170
+ return {
171
+ 'success': True,
172
+ 'message': f'Process {pid} killed successfully'
173
+ }
174
+ except psutil.NoSuchProcess:
175
+ return {
176
+ 'success': False,
177
+ 'message': f'No process found with PID {pid}'
178
+ }
179
+ except psutil.AccessDenied:
180
+ return {
181
+ 'success': False,
182
+ 'message': f'Access denied to kill process {pid}'
183
+ }
184
+ except Exception as e:
185
+ error_msg = f"Error killing process: {str(e)}"
186
+ logging.error(error_msg)
187
+ return {
188
+ 'success': False,
189
+ 'message': error_msg
190
+ }
191
+
192
+ def get_system_info(self) -> Dict[str, Union[float, int, str, Dict]]:
193
+ """Get system information.
194
+
195
+ Returns:
196
+ Dictionary with system information
197
+ """
198
+ try:
199
+ cpu_percent = psutil.cpu_percent(interval=1)
200
+ memory = psutil.virtual_memory()
201
+ disk = psutil.disk_usage('/')
202
+
203
+ return {
204
+ 'cpu': {
205
+ 'percent': cpu_percent,
206
+ 'cores': psutil.cpu_count(),
207
+ 'physical_cores': psutil.cpu_count(logical=False)
208
+ },
209
+ 'memory': {
210
+ 'total': memory.total,
211
+ 'available': memory.available,
212
+ 'percent': memory.percent,
213
+ 'used': memory.used,
214
+ 'free': memory.free
215
+ },
216
+ 'disk': {
217
+ 'total': disk.total,
218
+ 'used': disk.used,
219
+ 'free': disk.free,
220
+ 'percent': disk.percent
221
+ },
222
+ 'boot_time': psutil.boot_time(),
223
+ 'platform': os.uname().sysname
224
+ }
225
+ except Exception as e:
226
+ error_msg = f"Error getting system info: {str(e)}"
227
+ logging.error(error_msg)
228
+ return {}
229
+
230
+ # Create instance for direct function access
231
+ _shell_tools = ShellTools()
232
+ execute_command = _shell_tools.execute_command
233
+ list_processes = _shell_tools.list_processes
234
+ kill_process = _shell_tools.kill_process
235
+ get_system_info = _shell_tools.get_system_info
236
+
237
+ if __name__ == "__main__":
238
+ # Example usage
239
+ print("\n==================================================")
240
+ print("ShellTools Demonstration")
241
+ print("==================================================\n")
242
+
243
+ # 1. Execute command
244
+ print("1. Command Execution")
245
+ print("------------------------------")
246
+ result = execute_command("ls -la")
247
+ print(f"Success: {result['success']}")
248
+ print(f"Output:\n{result['stdout']}")
249
+ if result['stderr']:
250
+ print(f"Errors:\n{result['stderr']}")
251
+ print(f"Execution time: {result['execution_time']:.2f}s")
252
+ print()
253
+
254
+ # 2. System Information
255
+ print("2. System Information")
256
+ print("------------------------------")
257
+ info = get_system_info()
258
+ print(f"CPU Usage: {info['cpu']['percent']}%")
259
+ print(f"Memory Usage: {info['memory']['percent']}%")
260
+ print(f"Disk Usage: {info['disk']['percent']}%")
261
+ print(f"Platform: {info['platform']}")
262
+ print()
263
+
264
+ # 3. Process List
265
+ print("3. Process List (top 5 by CPU)")
266
+ print("------------------------------")
267
+ processes = sorted(
268
+ list_processes(),
269
+ key=lambda x: x['cpu_percent'],
270
+ reverse=True
271
+ )[:5]
272
+ for proc in processes:
273
+ print(f"PID: {proc['pid']}, Name: {proc['name']}, CPU: {proc['cpu_percent']}%")
274
+ print()
275
+
276
+ print("\n==================================================")
277
+ print("Demonstration Complete")
278
+ print("==================================================")