praisonaiagents 0.0.23__py3-none-any.whl → 0.0.24__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.
- praisonaiagents/tools/__init__.py +165 -2
- praisonaiagents/tools/arxiv_tools.py +292 -0
- praisonaiagents/tools/calculator_tools.py +278 -0
- praisonaiagents/tools/csv_tools.py +266 -0
- praisonaiagents/tools/duckdb_tools.py +268 -0
- praisonaiagents/tools/duckduckgo_tools.py +52 -0
- praisonaiagents/tools/excel_tools.py +310 -0
- praisonaiagents/tools/file_tools.py +274 -0
- praisonaiagents/tools/json_tools.py +515 -0
- praisonaiagents/tools/newspaper_tools.py +354 -0
- praisonaiagents/tools/pandas_tools.py +326 -0
- praisonaiagents/tools/python_tools.py +423 -0
- praisonaiagents/tools/shell_tools.py +278 -0
- praisonaiagents/tools/spider_tools.py +431 -0
- praisonaiagents/tools/test.py +56 -0
- praisonaiagents/tools/tools.py +5 -36
- praisonaiagents/tools/wikipedia_tools.py +272 -0
- praisonaiagents/tools/xml_tools.py +498 -0
- praisonaiagents/tools/yaml_tools.py +417 -0
- praisonaiagents/tools/yfinance_tools.py +213 -0
- {praisonaiagents-0.0.23.dist-info → praisonaiagents-0.0.24.dist-info}/METADATA +1 -1
- praisonaiagents-0.0.24.dist-info/RECORD +42 -0
- praisonaiagents-0.0.23.dist-info/RECORD +0 -24
- {praisonaiagents-0.0.23.dist-info → praisonaiagents-0.0.24.dist-info}/WHEEL +0 -0
- {praisonaiagents-0.0.23.dist-info → praisonaiagents-0.0.24.dist-info}/top_level.txt +0 -0
@@ -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("==================================================")
|