pysinfo 1.1__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.
pysinfo/__init__.py ADDED
@@ -0,0 +1,795 @@
1
+ """
2
+ PySInfo: A python command line tool that displays information about the current system, including hardware and critical software.
3
+ version 1.1
4
+ """
5
+ __version__ = "1.1.1"
6
+
7
+ import platform
8
+ import psutil
9
+ import os
10
+ import socket
11
+ import datetime
12
+ import distro
13
+ import shutil
14
+ import GPUtil
15
+ import colorama
16
+ import sys
17
+ import subprocess
18
+ import re
19
+ from colorama import Fore, Style, Back
20
+
21
+ def get_system_info():
22
+ info = {}
23
+
24
+ # OS Information
25
+ if platform.system() == "Windows":
26
+ info["os"] = f"{platform.system()} {platform.version()}"
27
+ elif platform.system() == "Darwin":
28
+ mac_ver = platform.mac_ver()[0]
29
+ info["os"] = f"macOS {mac_ver}"
30
+ else:
31
+ info["os"] = f"{distro.name()} {distro.version()}"
32
+
33
+ info["hostname"] = socket.gethostname()
34
+ info["kernel"] = platform.release()
35
+ info["uptime"] = get_uptime()
36
+
37
+ # Hardware Information - Enhanced CPU details
38
+ cpu_info = get_detailed_cpu_info()
39
+ info["cpu_model"] = cpu_info.get("model", platform.processor())
40
+ info["cpu_arch"] = cpu_info.get("architecture", platform.machine())
41
+ info["cpu_cores"] = f"{psutil.cpu_count(logical=False)} (Physical), {psutil.cpu_count(logical=True)} (Logical)"
42
+ info["cpu_freq"] = cpu_info.get("frequency", "Unknown")
43
+ info["cpu_cache"] = cpu_info.get("cache", "Unknown")
44
+ info["cpu_process"] = cpu_info.get("process", "Unknown")
45
+ info["cpu_usage"] = f"{psutil.cpu_percent()}%"
46
+
47
+ # Enhanced Memory Information
48
+ memory_info = get_detailed_memory_info()
49
+ mem = psutil.virtual_memory()
50
+ info["memory"] = f"{bytes_to_readable(mem.used)} / {bytes_to_readable(mem.total)} ({mem.percent}%)"
51
+ info["memory_slots"] = memory_info.get("slots", "Unknown")
52
+ info["memory_speed"] = memory_info.get("speed", "Unknown")
53
+
54
+ # Python Information
55
+ info["python_version"] = f"{platform.python_version()} ({platform.python_implementation()})"
56
+ info["python_path"] = sys.executable
57
+
58
+ # Try to get GPU info and CUDA support
59
+ try:
60
+ gpus = GPUtil.getGPUs()
61
+ if gpus:
62
+ gpu_info = []
63
+ for i, gpu in enumerate(gpus):
64
+ gpu_info.append(f"{gpu.name} ({bytes_to_readable(gpu.memoryTotal * 1024 * 1024)})")
65
+ info["gpu"] = " | ".join(gpu_info)
66
+
67
+ # Check CUDA support
68
+ cuda_version = check_cuda_version()
69
+ info["cuda"] = cuda_version if cuda_version else "Not detected"
70
+ else:
71
+ info["gpu"] = "No dedicated GPU detected"
72
+ info["cuda"] = "Not available"
73
+ except Exception as e:
74
+ if platform.system() == "Darwin":
75
+ result = subprocess.run(
76
+ ["system_profiler", "SPDisplaysDataType"],
77
+ capture_output=True,
78
+ text=True
79
+ )
80
+ matches = re.findall(r"Chipset Model:\s+(.*)", result.stdout)
81
+ info["gpu"] = " | ".join(matches) if matches else "Apple GPU"
82
+ info["cuda"] = "Not supported on macOS"
83
+ else:
84
+ info["gpu"] = "GPU information unavailable"
85
+ info["cuda"] = "Not detected"
86
+
87
+ # Check OpenCL support
88
+ info["opencl"] = check_opencl_version()
89
+
90
+ # Check Vulkan support
91
+ info["vulkan"] = check_vulkan_version()
92
+
93
+ # Disk Information
94
+ disk = psutil.disk_usage('/')
95
+ info["disk"] = f"{bytes_to_readable(disk.used)} / {bytes_to_readable(disk.total)} ({disk.percent}%)"
96
+
97
+ # Network Information
98
+ info["ip_address"] = socket.gethostbyname(socket.gethostname())
99
+
100
+ # Shell Information
101
+ if platform.system() == "Windows":
102
+ info["shell"] = os.environ.get("COMSPEC", "Unknown")
103
+ else:
104
+ info["shell"] = os.environ.get("SHELL", "Unknown")
105
+
106
+ # Terminal Information
107
+ terminal_size = shutil.get_terminal_size()
108
+ info["terminal"] = f"{terminal_size.columns}x{terminal_size.lines}"
109
+
110
+ return info
111
+
112
+ def get_detailed_cpu_info():
113
+ """Get more detailed CPU information."""
114
+ cpu_info = {
115
+ "model": platform.processor(),
116
+ "architecture": platform.machine(),
117
+ "frequency": psutil.cpu_freq().current if psutil.cpu_freq() else "Unknown",
118
+ "cores": f"{psutil.cpu_count(logical=False)} (Physical), {psutil.cpu_count(logical=True)} (Logical)",
119
+ "usage": f"{psutil.cpu_percent()}%"
120
+ }
121
+
122
+ try:
123
+ if platform.system() == "Windows":
124
+ # Try multiple methods to get CPU model name
125
+ model = None
126
+
127
+ # Method 1: Try using WMI
128
+ try:
129
+ result = subprocess.run(["wmic", "cpu", "get", "name"], capture_output=True, text=True)
130
+ if result.returncode == 0:
131
+ lines = result.stdout.strip().split('\n')
132
+ if len(lines) >= 2:
133
+ model = lines[1].strip()
134
+ except:
135
+ pass
136
+
137
+ # Method 2: Try using PowerShell if WMI fails
138
+ if not model or model == platform.processor():
139
+ try:
140
+ result = subprocess.run(["powershell", "-Command", "(Get-CimInstance -Class Win32_Processor).Name"],
141
+ capture_output=True, text=True)
142
+ if result.returncode == 0 and result.stdout.strip():
143
+ model = result.stdout.strip()
144
+ except:
145
+ pass
146
+
147
+ # Method 3: Registry as fallback
148
+ if not model or model == platform.processor():
149
+ try:
150
+ result = subprocess.run(["reg", "query", "HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
151
+ "/v", "ProcessorNameString"], capture_output=True, text=True)
152
+ if result.returncode == 0:
153
+ for line in result.stdout.split('\n'):
154
+ if "ProcessorNameString" in line:
155
+ model = line.split('REG_SZ')[1].strip()
156
+ except:
157
+ pass
158
+
159
+ if model and model != platform.processor():
160
+ cpu_info["model"] = model
161
+
162
+ # Rest of the function remains the same...
163
+ # Get CPU frequency
164
+
165
+ # Get CPU frequency
166
+ result = subprocess.run(["wmic", "cpu", "get", "MaxClockSpeed"], capture_output=True, text=True)
167
+ if result.returncode == 0:
168
+ lines = result.stdout.strip().split('\n')
169
+ if len(lines) >= 2:
170
+ freq_mhz = lines[1].strip()
171
+ if freq_mhz.isdigit():
172
+ cpu_info["frequency"] = f"{int(freq_mhz)/1000:.2f} GHz"
173
+
174
+ # Try to get cache information
175
+ result = subprocess.run(["wmic", "cpu", "get", "L2CacheSize,L3CacheSize"], capture_output=True, text=True)
176
+ if result.returncode == 0:
177
+ lines = result.stdout.strip().split('\n')
178
+ if len(lines) >= 2:
179
+ cache_parts = lines[1].strip().split()
180
+ if len(cache_parts) >= 2:
181
+ l2_cache = f"{int(cache_parts[0])/1024:.1f} MB" if cache_parts[0].isdigit() else "Unknown"
182
+ l3_cache = f"{int(cache_parts[1])/1024:.1f} MB" if cache_parts[1].isdigit() else "Unknown"
183
+ cpu_info["cache"] = f"L2: {l2_cache}, L3: {l3_cache}"
184
+
185
+ # Try to get CPU process information from model name
186
+ model = cpu_info["model"].lower()
187
+ if any(x in model for x in ["ryzen", "epyc"]):
188
+ if "3000" in model or "4000" in model or "5000" in model:
189
+ cpu_info["process"] = "7nm"
190
+ elif "6000" in model or "7000" in model:
191
+ cpu_info["process"] = "5nm"
192
+ elif "7945" in model:
193
+ cpu_info["process"] = "4nm"
194
+ elif any(x in model for x in ["intel", "core", "xeon"]):
195
+ if "10th" in model or "10" in model:
196
+ cpu_info["process"] = "14nm"
197
+ elif "11th" in model or "11" in model:
198
+ cpu_info["process"] = "10nm"
199
+ elif "12th" in model or "12" in model:
200
+ cpu_info["process"] = "Intel 7 (10nm)"
201
+ elif "13th" in model or "13" in model or "14th" in model or "14" in model:
202
+ cpu_info["process"] = "Intel 7 (10nm)"
203
+ elif platform.system() == "Darwin":
204
+ # CPU model
205
+ result = subprocess.run(
206
+ ["sysctl", "-n", "machdep.cpu.brand_string"],
207
+ capture_output=True,
208
+ text=True
209
+ )
210
+ if result.returncode == 0 and result.stdout.strip():
211
+ cpu_info["model"] = result.stdout.strip()
212
+ else:
213
+ # Apple Silicon fallback
214
+ result = subprocess.run(
215
+ ["sysctl", "-n", "hw.model"],
216
+ capture_output=True,
217
+ text=True
218
+ )
219
+ cpu_info["model"] = result.stdout.strip()
220
+
221
+ # CPU frequency
222
+ result = subprocess.run(
223
+ ["sysctl", "-n", "hw.cpufrequency"],
224
+ capture_output=True,
225
+ text=True
226
+ )
227
+ if result.returncode == 0 and result.stdout.strip().isdigit():
228
+ cpu_info["frequency"] = f"{int(result.stdout.strip()) / 1e9:.2f} GHz"
229
+
230
+ # Cache (best effort)
231
+ l2 = subprocess.run(["sysctl", "-n", "hw.l2cachesize"], capture_output=True, text=True)
232
+ l3 = subprocess.run(["sysctl", "-n", "hw.l3cachesize"], capture_output=True, text=True)
233
+ if l2.stdout.strip().isdigit() or l3.stdout.strip().isdigit():
234
+ cpu_info["cache"] = f"L2: {int(l2.stdout)/1024/1024:.1f} MB, L3: {int(l3.stdout)/1024/1024:.1f} MB"
235
+
236
+ # Apple Silicon process size
237
+ if platform.machine() == "arm64":
238
+ cpu_info["process"] = "5nm / 3nm (Apple Silicon)"
239
+ else:
240
+ # For Linux systems
241
+ if os.path.exists("/proc/cpuinfo"):
242
+ with open("/proc/cpuinfo", "r") as f:
243
+ cpuinfo = f.read()
244
+
245
+ # Extract model name
246
+ model_match = re.search(r"model name\s+:\s+(.*)", cpuinfo)
247
+ if model_match:
248
+ cpu_info["model"] = model_match.group(1)
249
+
250
+ # Extract CPU frequency
251
+ freq_match = re.search(r"cpu MHz\s+:\s+(.*)", cpuinfo)
252
+ if freq_match:
253
+ freq_mhz = float(freq_match.group(1))
254
+ cpu_info["frequency"] = f"{freq_mhz/1000:.2f} GHz"
255
+
256
+ # Extract cache info
257
+ cache_match = re.search(r"cache size\s+:\s+(.*)", cpuinfo)
258
+ if cache_match:
259
+ cpu_info["cache"] = cache_match.group(1)
260
+ except Exception as e:
261
+ pass
262
+
263
+ return cpu_info
264
+ def get_detailed_memory_info():
265
+ """Get detailed memory information including slots, type, speed, and timings."""
266
+ memory_info = {
267
+ "slots": "Unknown",
268
+ "type": "Unknown",
269
+ "speed": "Unknown",
270
+ "timings": "Unknown"
271
+ }
272
+
273
+ try:
274
+ if platform.system() == "Windows":
275
+ # Try multiple methods to get accurate memory information
276
+
277
+ # Method 1: Try using PowerShell (more reliable than WMIC)
278
+ try:
279
+ # Get memory details using Get-CimInstance
280
+ ps_cmd = "Get-CimInstance -ClassName Win32_PhysicalMemory | Select-Object Capacity, Speed, ConfiguredClockSpeed, DeviceLocator, SMBIOSMemoryType, PartNumber | ConvertTo-Json"
281
+ result = subprocess.run(["powershell", "-Command", ps_cmd],
282
+ capture_output=True, text=True, timeout=5)
283
+
284
+ if result.returncode == 0 and result.stdout.strip():
285
+ # Try to parse PowerShell output as JSON
286
+ import json
287
+ try:
288
+ mem_data = json.loads(result.stdout)
289
+ # Convert to list if single object is returned
290
+ if isinstance(mem_data, dict):
291
+ mem_data = [mem_data]
292
+
293
+ # Process memory data
294
+ if mem_data:
295
+ # Parse memory slots info
296
+ slots = []
297
+ speeds = []
298
+ configured_speeds = []
299
+ mem_types = []
300
+ part_numbers = []
301
+
302
+ for module in mem_data:
303
+ # Get capacity
304
+ if "Capacity" in module and module["Capacity"]:
305
+ capacity_gb = int(module["Capacity"]) / (1024**3)
306
+ slots.append(f"{capacity_gb:.0f} GB")
307
+
308
+ # Get memory speed
309
+ if "Speed" in module and module["Speed"]:
310
+ speeds.append(f"{module['Speed']} MHz")
311
+
312
+ # Get configured clock speed (often more accurate)
313
+ if "ConfiguredClockSpeed" in module and module["ConfiguredClockSpeed"]:
314
+ configured_speeds.append(f"{module['ConfiguredClockSpeed']} MHz")
315
+
316
+ # Get memory type
317
+ if "SMBIOSMemoryType" in module and module["SMBIOSMemoryType"]:
318
+ mem_type_code = str(module["SMBIOSMemoryType"])
319
+ mem_type_map = {
320
+ "0": "Unknown", "1": "Other", "2": "DRAM",
321
+ "3": "Synchronous DRAM", "4": "Cache DRAM",
322
+ "5": "EDO", "6": "EDRAM", "7": "VRAM",
323
+ "8": "SRAM", "9": "RAM", "10": "ROM",
324
+ "11": "Flash", "12": "EEPROM", "13": "FEPROM",
325
+ "14": "EPROM", "15": "CDRAM", "16": "3DRAM",
326
+ "17": "SDRAM", "18": "SGRAM", "19": "RDRAM",
327
+ "20": "DDR", "21": "DDR2", "22": "DDR2 FB-DIMM",
328
+ "24": "DDR3", "26": "DDR4", "27": "DDR5",
329
+ "28": "LPDDR", "29": "LPDDR2", "30": "LPDDR3",
330
+ "31": "LPDDR4", "32": "LPDDR5"
331
+ }
332
+ mem_types.append(mem_type_map.get(mem_type_code, "Unknown"))
333
+
334
+ # Get part number (might contain timing info)
335
+ if "PartNumber" in module and module["PartNumber"]:
336
+ part_num = module["PartNumber"].strip()
337
+ if part_num:
338
+ part_numbers.append(part_num)
339
+
340
+ # Set memory info based on collected data
341
+ if slots:
342
+ memory_info["slots"] = f"{len(slots)} ({', '.join(slots)})"
343
+
344
+ # Prefer configured speeds if available
345
+ if configured_speeds and len(set(configured_speeds)) == 1:
346
+ memory_info["speed"] = configured_speeds[0]
347
+ elif configured_speeds:
348
+ memory_info["speed"] = ", ".join(configured_speeds)
349
+ elif speeds and len(set(speeds)) == 1:
350
+ memory_info["speed"] = speeds[0]
351
+ elif speeds:
352
+ memory_info["speed"] = ", ".join(speeds)
353
+
354
+ # Set memory type
355
+ if mem_types and len(set(mem_types)) == 1 and mem_types[0] != "Unknown":
356
+ memory_info["type"] = mem_types[0]
357
+ elif mem_types:
358
+ memory_info["type"] = ", ".join(set(mem_types))
359
+
360
+ # Try to extract timing info from part numbers
361
+ if part_numbers:
362
+ for part in part_numbers:
363
+ # Look for common timing patterns in part numbers
364
+ timing_match = re.search(r"[^0-9](\d{1,2})-(\d{1,2})-(\d{1,2})(?:-(\d{1,2}))?", part)
365
+ if timing_match:
366
+ if timing_match.group(4):
367
+ memory_info["timings"] = f"CL{timing_match.group(1)}-{timing_match.group(2)}-{timing_match.group(3)}-{timing_match.group(4)}"
368
+ else:
369
+ memory_info["timings"] = f"CL{timing_match.group(1)}-{timing_match.group(2)}-{timing_match.group(3)}"
370
+ break
371
+
372
+ # Second pattern attempt
373
+ cl_match = re.search(r"CL(\d{1,2})", part, re.IGNORECASE)
374
+ if cl_match:
375
+ memory_info["timings"] = f"CL{cl_match.group(1)} (partial)"
376
+ break
377
+ except json.JSONDecodeError:
378
+ pass
379
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired):
380
+ pass
381
+
382
+ # Method 2: Fall back to WMIC if PowerShell approach failed
383
+ if memory_info["slots"] == "Unknown":
384
+ # Use the existing WMIC implementation
385
+ result = subprocess.run(["wmic", "memorychip", "get", "Capacity,Speed,DeviceLocator,MemoryType"],
386
+ capture_output=True, text=True)
387
+ if result.returncode == 0:
388
+ lines = result.stdout.strip().split('\n')
389
+ if len(lines) >= 2:
390
+ # Parse memory slots info
391
+ slots = []
392
+ speeds = []
393
+ mem_types = []
394
+
395
+ for line in lines[1:]:
396
+ if line.strip():
397
+ parts = line.strip().split()
398
+ if len(parts) >= 3:
399
+ try:
400
+ capacity = int(parts[0])
401
+ slots.append(f"{capacity / (1024**3):.0f} GB")
402
+
403
+ if parts[-2].isdigit():
404
+ speeds.append(f"{parts[-2]} MHz")
405
+
406
+ # Convert memory type code to human-readable
407
+ mem_type_code = parts[-1] if parts[-1].isdigit() else "0"
408
+ mem_type = "Unknown"
409
+ mem_type_map = {
410
+ "0": "Unknown", "1": "Other", "2": "DRAM",
411
+ "3": "Synchronous DRAM", "4": "Cache DRAM",
412
+ "5": "EDO", "6": "EDRAM", "7": "VRAM",
413
+ "8": "SRAM", "9": "RAM", "10": "ROM",
414
+ "11": "Flash", "12": "EEPROM", "13": "FEPROM",
415
+ "14": "EPROM", "15": "CDRAM", "16": "3DRAM",
416
+ "17": "SDRAM", "18": "SGRAM", "19": "RDRAM",
417
+ "20": "DDR", "21": "DDR2", "22": "DDR2 FB-DIMM",
418
+ "24": "DDR3", "26": "DDR4", "27": "DDR5",
419
+ "28": "LPDDR", "29": "LPDDR2", "30": "LPDDR3",
420
+ "31": "LPDDR4", "32": "LPDDR5"
421
+ }
422
+ mem_types.append(mem_type_map.get(mem_type_code, "Unknown"))
423
+ except:
424
+ pass
425
+
426
+ if slots:
427
+ memory_info["slots"] = f"{len(slots)} ({', '.join(slots)})"
428
+ if speeds and len(set(speeds)) == 1:
429
+ memory_info["speed"] = speeds[0]
430
+ elif speeds:
431
+ memory_info["speed"] = ', '.join(speeds)
432
+ if mem_types and len(set(mem_types)) == 1 and mem_types[0] != "Unknown":
433
+ memory_info["type"] = mem_types[0]
434
+
435
+ # Method 3: Use Windows Management Instrumentation Command-line (WMIC)
436
+ # to get SPD data if timing info is still unknown
437
+ if memory_info["timings"] == "Unknown":
438
+ try:
439
+ # Try to get SPD info using WMI
440
+ ps_cmd = """
441
+ $SPDInfoClass = Get-WmiObject -Class MSMemory_MemoryDevice
442
+ $SPDInfoClass | Select-Object DataWidth, TotalWidth, Speed | ConvertTo-Json
443
+ """
444
+ result = subprocess.run(["powershell", "-Command", ps_cmd],
445
+ capture_output=True, text=True, timeout=5)
446
+
447
+ if result.returncode == 0 and result.stdout.strip():
448
+ # Try to determine memory timings from detailed memory specs
449
+ if memory_info["type"] != "Unknown" and memory_info["speed"] != "Unknown":
450
+ # Get the speed in MHz
451
+ speed_match = re.search(r"(\d+)\s*MHz", memory_info["speed"])
452
+ if speed_match:
453
+ speed_mhz = int(speed_match.group(1))
454
+
455
+ # Determine memory timings based on type and speed
456
+ if "DDR4" in memory_info["type"]:
457
+ if speed_mhz >= 3600:
458
+ memory_info["timings"] = "CL16-18-18-38 (typical)"
459
+ elif speed_mhz >= 3200:
460
+ memory_info["timings"] = "CL16-18-18-36 (typical)"
461
+ elif speed_mhz >= 2666:
462
+ memory_info["timings"] = "CL16-18-18-35 (typical)"
463
+ else:
464
+ memory_info["timings"] = "CL16-16-16-32 (typical)"
465
+ elif "DDR5" in memory_info["type"]:
466
+ if speed_mhz >= 6000:
467
+ memory_info["timings"] = "CL40-40-40-76 (typical)"
468
+ elif speed_mhz >= 5600:
469
+ memory_info["timings"] = "CL36-36-36-76 (typical)"
470
+ elif speed_mhz >= 4800:
471
+ memory_info["timings"] = "CL34-34-34-68 (typical)"
472
+ else:
473
+ memory_info["timings"] = "CL30-30-30-60 (typical)"
474
+ elif "DDR3" in memory_info["type"]:
475
+ if speed_mhz >= 2133:
476
+ memory_info["timings"] = "CL15-15-15-35 (typical)"
477
+ elif speed_mhz >= 1866:
478
+ memory_info["timings"] = "CL13-13-13-32 (typical)"
479
+ elif speed_mhz >= 1600:
480
+ memory_info["timings"] = "CL11-11-11-28 (typical)"
481
+ else:
482
+ memory_info["timings"] = "CL9-9-9-24 (typical)"
483
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired):
484
+ pass
485
+
486
+ # Method 4: Get memory details from CPU-Z report if available (Windows only)
487
+ if memory_info["type"] == "Unknown" or memory_info["timings"] == "Unknown":
488
+ try:
489
+ # Check if CPU-Z is installed (common location)
490
+ cpuz_paths = [
491
+ os.path.join(os.environ.get('ProgramFiles', ''), 'CPUID\\CPU-Z\\cpuz.exe'),
492
+ os.path.join(os.environ.get('ProgramFiles(x86)', ''), 'CPUID\\CPU-Z\\cpuz.exe')
493
+ ]
494
+
495
+ cpuz_installed = any(os.path.exists(path) for path in cpuz_paths)
496
+
497
+ if cpuz_installed:
498
+ # CPU-Z is installed, we could potentially run it in command line mode
499
+ # This is a placeholder - CPU-Z CLI options are limited
500
+ pass
501
+ except:
502
+ pass
503
+ elif platform.system() == "Darwin":
504
+ mem = psutil.virtual_memory()
505
+ memory_info["slots"] = "Unified Memory"
506
+ memory_info["type"] = "LPDDR (Apple Silicon)" if platform.machine() == "arm64" else "DDR"
507
+ memory_info["speed"] = "Unified (SoC)"
508
+ memory_info["timings"] = "N/A"
509
+ else:
510
+ # The Linux section remains the same
511
+ # For Linux systems
512
+ if os.path.exists("/proc/meminfo"):
513
+ # Try using dmidecode if available
514
+ try:
515
+ # This requires sudo permissions - may not work without them
516
+ result = subprocess.run(["sudo", "dmidecode", "--type", "memory"], capture_output=True, text=True)
517
+ if result.returncode == 0:
518
+ output = result.stdout
519
+
520
+ # Count memory slots
521
+ slot_count = output.count("Memory Device")
522
+ if slot_count > 0:
523
+ memory_info["slots"] = str(slot_count)
524
+
525
+ # Look for type
526
+ type_match = re.search(r"Type: (DDR\d+)", output)
527
+ if type_match:
528
+ memory_info["type"] = type_match.group(1)
529
+
530
+ # Look for speed
531
+ speed_match = re.search(r"Speed: (\d+ MHz)", output)
532
+ if speed_match:
533
+ memory_info["speed"] = speed_match.group(1)
534
+ except:
535
+ pass
536
+ except Exception as e:
537
+ pass
538
+
539
+ return memory_info
540
+ def check_cuda_version():
541
+ try:
542
+ if platform.system() == "Windows":
543
+ # Try to get CUDA version from nvcc
544
+ result = subprocess.run(["nvcc", "--version"], capture_output=True, text=True)
545
+ if result.returncode == 0:
546
+ for line in result.stdout.split('\n'):
547
+ if "release" in line.lower() and "V" in line:
548
+ return line.split("V")[1].split(" ")[0]
549
+ else:
550
+ # For Linux/macOS
551
+ cuda_path = "/usr/local/cuda/bin/nvcc"
552
+ if os.path.exists(cuda_path):
553
+ result = subprocess.run([cuda_path, "--version"], capture_output=True, text=True)
554
+ if result.returncode == 0:
555
+ for line in result.stdout.split('\n'):
556
+ if "release" in line.lower() and "V" in line:
557
+ return line.split("V")[1].split(" ")[0]
558
+
559
+ # Alternative check with nvidia-smi for CUDA driver version
560
+ result = subprocess.run(["nvidia-smi"], capture_output=True, text=True)
561
+ if result.returncode == 0:
562
+ for line in result.stdout.split('\n'):
563
+ if "CUDA Version:" in line:
564
+ return line.split("CUDA Version:")[1].strip()
565
+
566
+ return "Not detected"
567
+ except:
568
+ return "Not detected"
569
+
570
+ def check_opencl_version():
571
+ try:
572
+ # Try to detect OpenCL with a simple command
573
+ if platform.system() == "Windows":
574
+ result = subprocess.run(["clinfo", "--version"], capture_output=True, text=True)
575
+ if result.returncode == 0:
576
+ for line in result.stdout.split('\n'):
577
+ if "OpenCL" in line:
578
+ return line.strip()
579
+ elif platform.system() == "Darwin":
580
+ return "OpenCL supported (Apple framework)"
581
+ else:
582
+ # For Linux
583
+ if os.path.exists("/usr/bin/clinfo"):
584
+ result = subprocess.run(["clinfo", "--version"], capture_output=True, text=True)
585
+ if result.returncode == 0:
586
+ for line in result.stdout.split('\n'):
587
+ if "OpenCL" in line:
588
+ return line.strip()
589
+
590
+ return "Not detected"
591
+ except:
592
+ return "Not detected"
593
+
594
+ def check_vulkan_version():
595
+ try:
596
+ # Try to detect Vulkan with vulkaninfo
597
+ if platform.system() == "Windows":
598
+ result = subprocess.run(["vulkaninfo", "--summary"], capture_output=True, text=True)
599
+ elif platform.system() == "Darwin":
600
+ return "Not supported (use MoltenVK)"
601
+ else:
602
+ result = subprocess.run(["vulkaninfo"], capture_output=True, text=True)
603
+
604
+ if result.returncode == 0:
605
+ for line in result.stdout.split('\n'):
606
+ if "Vulkan Instance Version:" in line:
607
+ return line.split("Vulkan Instance Version:")[1].strip()
608
+ if "Vulkan API Version:" in line:
609
+ return line.split("Vulkan API Version:")[1].strip()
610
+
611
+ return "Not detected"
612
+ except:
613
+ return "Not detected"
614
+
615
+ def get_uptime():
616
+ if platform.system() == "Windows":
617
+ boot_time = datetime.datetime.fromtimestamp(psutil.boot_time())
618
+ elif platform.system() == "Darwin":
619
+ result = subprocess.run(
620
+ ["sysctl", "-n", "kern.boottime"],
621
+ capture_output=True,
622
+ text=True
623
+ )
624
+ match = re.search(r"sec = (\d+)", result.stdout)
625
+ boot_time = datetime.datetime.fromtimestamp(int(match.group(1))) if match else datetime.datetime.now()
626
+ else:
627
+ with open('/proc/uptime', 'r') as f:
628
+ uptime_seconds = float(f.readline().split()[0])
629
+ boot_time = datetime.datetime.now() - datetime.timedelta(seconds=uptime_seconds)
630
+
631
+ uptime = datetime.datetime.now() - boot_time
632
+ days, remainder = divmod(uptime.total_seconds(), 86400)
633
+ hours, remainder = divmod(remainder, 3600)
634
+ minutes, _ = divmod(remainder, 60)
635
+ return f"{int(days)} days, {int(hours)} hours, {int(minutes)} minutes"
636
+
637
+ def bytes_to_readable(bytes):
638
+ for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
639
+ if bytes < 1024:
640
+ return f"{bytes:.2f} {unit}"
641
+ bytes /= 1024
642
+ return f"{bytes:.2f} PB"
643
+
644
+ def get_ascii_logo(system):
645
+ logos = {
646
+ "Windows": [
647
+ " ..,",
648
+ " ....,,:;+ccllll",
649
+ " ...,,+:; cllllllllllllllllll",
650
+ ",cclllllllllll lllllllllllllllllll",
651
+ "llllllllllllll lllllllllllllllllll",
652
+ "llllllllllllll lllllllllllllllllll",
653
+ "llllllllllllll lllllllllllllllllll",
654
+ "llllllllllllll lllllllllllllllllll",
655
+ "llllllllllllll lllllllllllllllllll",
656
+ " ",
657
+ "llllllllllllll lllllllllllllllllll",
658
+ "llllllllllllll lllllllllllllllllll",
659
+ "llllllllllllll lllllllllllllllllll",
660
+ "llllllllllllll lllllllllllllllllll",
661
+ "llllllllllllll lllllllllllllllllll",
662
+ "`'ccllllllllll lllllllllllllllllll",
663
+ " `' \\*:: :ccllllllllllllllll",
664
+ " ````''*::cll",
665
+ " ``",
666
+ ],
667
+ "Linux": [
668
+ " _nnnn_ ",
669
+ " dGGGGMMb ",
670
+ " @p~qp~~qMb ",
671
+ " M|@||@) M| ",
672
+ " @,----.JM| ",
673
+ " JS^\\__/ qKL ",
674
+ " dZP qKRb ",
675
+ " dZP qKKb ",
676
+ " fZP SMMb ",
677
+ " HZM MMMM ",
678
+ " FqM MMMM ",
679
+ " __| \". |\\dS\"qML ",
680
+ " | `. | `' \\Zq ",
681
+ "_) \\.___.,| .' ",
682
+ "\\____ )MMMMMP| .' ",
683
+ " `-' `--' ",
684
+ ],
685
+ "Darwin": [
686
+ " ##### ",
687
+ " ###### ",
688
+ " ###### ",
689
+ " ####### ",
690
+ " ####### ",
691
+ " ####### ",
692
+ " ####### ",
693
+ " ####### ##### ",
694
+ " ####### ####### ",
695
+ " ################## ",
696
+ " ################# ",
697
+ " ################ ",
698
+ " ############### ",
699
+ " ############## ",
700
+ " ############# ",
701
+ " ############ ",
702
+ " ########### ",
703
+ ],
704
+ }
705
+
706
+ default_logo = [
707
+ " _____ _____ __ ",
708
+ " | __ \\ / ____| / _| ",
709
+ " | |__) | | (___ ___ | |_ ___ ",
710
+ " | ___/ | | \\___ \\ / _ \\| _/ _ \\ ",
711
+ " | | | |_| |___) | (_) | || (_) | ",
712
+ " |_| \\__, |____/ \\___/|_| \\___/ ",
713
+ " __/ | ",
714
+ " |___/ ",
715
+ ]
716
+
717
+ return logos.get(system, default_logo)
718
+
719
+ def print_system_info():
720
+ colorama.init()
721
+ system_info = get_system_info()
722
+ ascii_logo = get_ascii_logo(platform.system())
723
+
724
+ # ANSI Color codes for different OS
725
+ color = Fore.CYAN # Default color
726
+ if platform.system() == "Windows":
727
+ color = Fore.BLUE
728
+ elif platform.system() == "Linux":
729
+ color = Fore.GREEN
730
+ elif platform.system() == "Darwin":
731
+ color = Fore.WHITE
732
+
733
+ # Header
734
+ print(f"\n{Style.BRIGHT}{Back.BLACK}{color}╒════════════════════════════════════════════════════════════════╕{Style.RESET_ALL}")
735
+ print(f"{Style.BRIGHT}{Back.BLACK}{color}│ PySinfo: A Python Tool to Get System Infomation │{Style.RESET_ALL}")
736
+ print(f"{Style.BRIGHT}{Back.BLACK}{color}╘════════════════════════════════════════════════════════════════╛{Style.RESET_ALL}\n")
737
+
738
+ # Prepare data display
739
+ data_lines = [
740
+ f"{color}OS:{Style.RESET_ALL} {system_info['os']}",
741
+ f"{color}Host:{Style.RESET_ALL} {system_info['hostname']}",
742
+ f"{color}Kernel:{Style.RESET_ALL} {system_info['kernel']}",
743
+ f"{color}Uptime:{Style.RESET_ALL} {system_info['uptime']}",
744
+ f"{color}Shell:{Style.RESET_ALL} {os.path.basename(system_info['shell'])}",
745
+ f"{color}Terminal:{Style.RESET_ALL} {system_info['terminal']}",
746
+ "",
747
+ f"{Style.BRIGHT}{color}Hardware:{Style.RESET_ALL}",
748
+ f"{color}CPU Model:{Style.RESET_ALL} {system_info['cpu_model']}",
749
+ f"{color}CPU Architecture:{Style.RESET_ALL} {system_info['cpu_arch']}",
750
+ f"{color}CPU Current Freq:{Style.RESET_ALL} {system_info['cpu_freq']}",
751
+ f"{color}CPU Cores:{Style.RESET_ALL} {system_info['cpu_cores']}",
752
+ f"{color}CPU Usage:{Style.RESET_ALL} {system_info['cpu_usage']}",
753
+ f"{color}GPU:{Style.RESET_ALL} {system_info['gpu']}",
754
+ f"{color}Memory:{Style.RESET_ALL} {system_info['memory']}",
755
+ f"{color}Memory Slots:{Style.RESET_ALL} {system_info['memory_slots']}",
756
+ f"{color}Memory Speed:{Style.RESET_ALL} {system_info['memory_speed']}",
757
+ f"{color}Disk:{Style.RESET_ALL} {system_info['disk']}",
758
+ "",
759
+ f"{Style.BRIGHT}{color}Network:{Style.RESET_ALL}",
760
+ f"{color}IP Address:{Style.RESET_ALL} {system_info['ip_address']}",
761
+ "",
762
+ f"{Style.BRIGHT}{color}Development Environment:{Style.RESET_ALL}",
763
+ f"{color}Python:{Style.RESET_ALL} {system_info['python_version']}",
764
+ f"{color}Python Path:{Style.RESET_ALL} {system_info['python_path']}",
765
+ "",
766
+ f"{Style.BRIGHT}{color}Graphics API Support:{Style.RESET_ALL}",
767
+ f"{color}CUDA:{Style.RESET_ALL} {system_info['cuda']}",
768
+ f"{color}OpenCL:{Style.RESET_ALL} {system_info['opencl']}",
769
+ f"{color}Vulkan:{Style.RESET_ALL} {system_info['vulkan']}",
770
+ ]
771
+
772
+ # Calculate spacing
773
+ logo_width = max(len(line) for line in ascii_logo)
774
+ spacing = 4
775
+
776
+ # Print logo and info side by side
777
+ logo_length = len(ascii_logo)
778
+ for i in range(min(logo_length, len(data_lines))):
779
+ logo_line = ascii_logo[i]
780
+ info_line = data_lines[i]
781
+ print(f"{color}{logo_line}{' ' * spacing}{Style.RESET_ALL}{info_line}")
782
+
783
+ # Print remaining info
784
+ for i in range(logo_length, len(data_lines)):
785
+ print(f"{' ' * (logo_width + spacing)}{data_lines[i]}")
786
+
787
+ # Print color blocks
788
+ print("\n" + "".join(f"{color}██████{Style.RESET_ALL}" for color in [Fore.RED, Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.WHITE]))
789
+
790
+ # Footer
791
+ current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
792
+ print(f"\n{Style.DIM}Report generated on: {current_time}{Style.RESET_ALL}")
793
+
794
+ if __name__ == "__main__":
795
+ print_system_info()