Skip to content

Commit caf65a4

Browse files
authored
Merge pull request #10 from errnair/errnair/modernize-python-scripts
Modernize Python scripts (checkcpu.py and timer.py) LGTM
2 parents 0c2c5c2 + 7761f90 commit caf65a4

File tree

2 files changed

+696
-16
lines changed

2 files changed

+696
-16
lines changed

python-scripts/checkcpu.py

Lines changed: 348 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,352 @@
1-
#!/usr/bin/env python
1+
#!/usr/bin/env python3
22
"""
3-
Script to check the number of CPU cores.
4-
Usage: python checkcpu.py
3+
CPU Information Tool
4+
5+
Displays comprehensive CPU information including count, model, frequency,
6+
cache sizes, and usage statistics.
7+
8+
Usage:
9+
python3 checkcpu.py [options]
510
"""
11+
12+
import argparse
13+
import json
614
import multiprocessing
15+
import os
16+
import platform
17+
import sys
18+
from typing import Dict, Any, Optional
19+
20+
21+
def get_cpu_count() -> Dict[str, int]:
22+
"""Get CPU count information."""
23+
logical_count = multiprocessing.cpu_count()
24+
25+
# Try to get physical core count
26+
try:
27+
physical_count = os.cpu_count() or logical_count
28+
except AttributeError:
29+
physical_count = logical_count
30+
31+
return {
32+
'logical': logical_count,
33+
'physical': physical_count
34+
}
35+
36+
37+
def get_cpu_info_linux() -> Dict[str, Any]:
38+
"""Get detailed CPU information on Linux."""
39+
info = {}
40+
41+
try:
42+
with open('/proc/cpuinfo', 'r') as f:
43+
cpuinfo = f.read()
44+
45+
# Extract model name
46+
for line in cpuinfo.split('\n'):
47+
if 'model name' in line:
48+
info['model'] = line.split(':')[1].strip()
49+
break
50+
51+
# Extract vendor
52+
for line in cpuinfo.split('\n'):
53+
if 'vendor_id' in line:
54+
info['vendor'] = line.split(':')[1].strip()
55+
break
56+
57+
# Extract CPU MHz
58+
for line in cpuinfo.split('\n'):
59+
if 'cpu MHz' in line:
60+
info['mhz'] = float(line.split(':')[1].strip())
61+
break
62+
63+
# Extract cache size
64+
for line in cpuinfo.split('\n'):
65+
if 'cache size' in line:
66+
info['cache_size'] = line.split(':')[1].strip()
67+
break
68+
69+
# Extract flags/features (first processor only)
70+
for line in cpuinfo.split('\n'):
71+
if 'flags' in line:
72+
flags = line.split(':')[1].strip().split()
73+
info['flags'] = flags
74+
info['flag_count'] = len(flags)
75+
break
76+
77+
except FileNotFoundError:
78+
pass
79+
80+
return info
81+
82+
83+
def get_cpu_info_macos() -> Dict[str, Any]:
84+
"""Get detailed CPU information on macOS."""
85+
info = {}
86+
87+
try:
88+
import subprocess
89+
90+
# Get CPU brand
91+
result = subprocess.run(
92+
['sysctl', '-n', 'machdep.cpu.brand_string'],
93+
capture_output=True,
94+
text=True,
95+
check=True
96+
)
97+
info['model'] = result.stdout.strip()
98+
99+
# Get CPU frequency
100+
try:
101+
result = subprocess.run(
102+
['sysctl', '-n', 'hw.cpufrequency'],
103+
capture_output=True,
104+
text=True,
105+
check=True
106+
)
107+
if result.stdout.strip():
108+
info['hz'] = int(result.stdout.strip())
109+
info['mhz'] = info['hz'] / 1_000_000
110+
except (subprocess.CalledProcessError, ValueError):
111+
pass
112+
113+
# Get cache sizes
114+
for cache_type in ['l1icachesize', 'l1dcachesize', 'l2cachesize', 'l3cachesize']:
115+
try:
116+
result = subprocess.run(
117+
['sysctl', '-n', f'hw.{cache_type}'],
118+
capture_output=True,
119+
text=True,
120+
check=True
121+
)
122+
cache_bytes = int(result.stdout.strip())
123+
cache_kb = cache_bytes / 1024
124+
info[cache_type] = f"{cache_kb:.0f} KB"
125+
except (subprocess.CalledProcessError, ValueError):
126+
pass
127+
128+
# Get CPU features
129+
try:
130+
result = subprocess.run(
131+
['sysctl', '-n', 'machdep.cpu.features'],
132+
capture_output=True,
133+
text=True,
134+
check=True
135+
)
136+
flags = result.stdout.strip().split()
137+
info['flags'] = flags
138+
info['flag_count'] = len(flags)
139+
except subprocess.CalledProcessError:
140+
pass
141+
142+
except (ImportError, FileNotFoundError):
143+
pass
144+
145+
return info
146+
147+
148+
def get_cpu_usage() -> Optional[float]:
149+
"""Get current CPU usage percentage."""
150+
try:
151+
import psutil
152+
return psutil.cpu_percent(interval=1)
153+
except ImportError:
154+
# Try Linux-specific method
155+
try:
156+
with open('/proc/stat', 'r') as f:
157+
lines = f.readlines()
158+
159+
# First line is aggregate CPU stats
160+
cpu_line = lines[0].split()
161+
162+
# Calculate usage (simplified)
163+
user = int(cpu_line[1])
164+
nice = int(cpu_line[2])
165+
system = int(cpu_line[3])
166+
idle = int(cpu_line[4])
167+
168+
total = user + nice + system + idle
169+
used = user + nice + system
170+
171+
return (used / total) * 100 if total > 0 else 0.0
172+
except (FileNotFoundError, IndexError, ValueError):
173+
return None
174+
175+
176+
def get_cpu_temperature() -> Optional[float]:
177+
"""Get CPU temperature if available."""
178+
try:
179+
import psutil
180+
temps = psutil.sensors_temperatures()
181+
182+
# Try common sensor names
183+
for sensor_name in ['coretemp', 'cpu_thermal', 'k10temp']:
184+
if sensor_name in temps:
185+
return temps[sensor_name][0].current
186+
187+
# If no common sensor found, try first available
188+
if temps:
189+
first_sensor = list(temps.keys())[0]
190+
return temps[first_sensor][0].current
191+
except (ImportError, AttributeError):
192+
pass
193+
194+
return None
195+
196+
197+
def is_virtual_cpu() -> bool:
198+
"""Detect if running on a virtual machine."""
199+
system = platform.system()
200+
201+
if system == 'Linux':
202+
try:
203+
with open('/proc/cpuinfo', 'r') as f:
204+
cpuinfo = f.read().lower()
205+
206+
# Check for hypervisor flags
207+
if 'hypervisor' in cpuinfo:
208+
return True
209+
210+
# Check for virtual CPU models
211+
virtual_indicators = ['qemu', 'kvm', 'virtual', 'vmware', 'xen']
212+
for indicator in virtual_indicators:
213+
if indicator in cpuinfo:
214+
return True
215+
except FileNotFoundError:
216+
pass
217+
218+
return False
219+
220+
221+
def format_text_output(cpu_data: Dict[str, Any], verbose: bool = False) -> str:
222+
"""Format CPU information as text."""
223+
lines = []
224+
225+
lines.append("CPU Information")
226+
lines.append("=" * 50)
227+
lines.append("")
228+
229+
# Basic info
230+
lines.append(f"Logical CPUs: {cpu_data['count']['logical']}")
231+
lines.append(f"Physical CPUs: {cpu_data['count']['physical']}")
232+
233+
if cpu_data.get('model'):
234+
lines.append(f"Model: {cpu_data['model']}")
235+
236+
if cpu_data.get('vendor'):
237+
lines.append(f"Vendor: {cpu_data['vendor']}")
238+
239+
if cpu_data.get('mhz'):
240+
lines.append(f"Frequency: {cpu_data['mhz']:.2f} MHz")
241+
242+
if cpu_data.get('cache_size'):
243+
lines.append(f"Cache Size: {cpu_data['cache_size']}")
244+
245+
# Cache info (macOS)
246+
for cache in ['l1icachesize', 'l1dcachesize', 'l2cachesize', 'l3cachesize']:
247+
if cpu_data.get(cache):
248+
cache_name = cache.replace('cachesize', '').upper()
249+
lines.append(f"{cache_name} Cache: {cpu_data[cache]}")
250+
251+
if cpu_data.get('virtual') is not None:
252+
lines.append(f"Virtual CPU: {'Yes' if cpu_data['virtual'] else 'No'}")
253+
254+
if cpu_data.get('usage') is not None:
255+
lines.append(f"CPU Usage: {cpu_data['usage']:.1f}%")
256+
257+
if cpu_data.get('temperature') is not None:
258+
lines.append(f"Temperature: {cpu_data['temperature']:.1f}°C")
259+
260+
lines.append(f"Platform: {cpu_data['platform']}")
261+
lines.append(f"Architecture: {cpu_data['architecture']}")
262+
263+
if verbose and cpu_data.get('flags'):
264+
lines.append("")
265+
lines.append(f"CPU Flags ({cpu_data.get('flag_count', 0)}):")
266+
lines.append("-" * 50)
267+
268+
# Display flags in columns
269+
flags = cpu_data['flags']
270+
col_width = 15
271+
cols = 4
272+
273+
for i in range(0, len(flags), cols):
274+
row_flags = flags[i:i+cols]
275+
line = " ".join(f"{flag:<{col_width}}" for flag in row_flags)
276+
lines.append(f" {line}")
277+
elif cpu_data.get('flag_count'):
278+
lines.append(f"CPU Flags: {cpu_data['flag_count']} features")
279+
280+
return "\n".join(lines)
281+
282+
283+
def format_json_output(cpu_data: Dict[str, Any]) -> str:
284+
"""Format CPU information as JSON."""
285+
return json.dumps(cpu_data, indent=2)
286+
287+
288+
def main():
289+
"""Main entry point."""
290+
parser = argparse.ArgumentParser(
291+
description='Display detailed CPU information',
292+
formatter_class=argparse.RawDescriptionHelpFormatter,
293+
epilog="""
294+
Examples:
295+
%(prog)s # Display CPU information
296+
%(prog)s --json # JSON output
297+
%(prog)s --verbose # Show all CPU flags/features
298+
%(prog)s -v --json # Verbose JSON output
299+
"""
300+
)
301+
302+
parser.add_argument(
303+
'--json',
304+
action='store_true',
305+
help='Output in JSON format'
306+
)
307+
308+
parser.add_argument(
309+
'-v', '--verbose',
310+
action='store_true',
311+
help='Show detailed CPU flags and features'
312+
)
313+
314+
args = parser.parse_args()
315+
316+
# Gather CPU information
317+
cpu_data = {
318+
'count': get_cpu_count(),
319+
'platform': platform.system(),
320+
'architecture': platform.machine(),
321+
}
322+
323+
# Get platform-specific info
324+
system = platform.system()
325+
if system == 'Linux':
326+
cpu_data.update(get_cpu_info_linux())
327+
elif system == 'Darwin':
328+
cpu_data.update(get_cpu_info_macos())
329+
330+
# Get dynamic info
331+
cpu_data['usage'] = get_cpu_usage()
332+
cpu_data['temperature'] = get_cpu_temperature()
333+
cpu_data['virtual'] = is_virtual_cpu()
334+
335+
# Output
336+
if args.json:
337+
# If not verbose, remove flags from JSON
338+
if not args.verbose and 'flags' in cpu_data:
339+
flag_count = cpu_data.get('flag_count')
340+
del cpu_data['flags']
341+
if flag_count:
342+
cpu_data['flag_count'] = flag_count
343+
344+
print(format_json_output(cpu_data))
345+
else:
346+
print(format_text_output(cpu_data, verbose=args.verbose))
347+
348+
return 0
349+
7350

8-
cpu_count = multiprocessing.cpu_count()
9-
print (cpu_count)
351+
if __name__ == '__main__':
352+
sys.exit(main())

0 commit comments

Comments
 (0)