-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtime.py
More file actions
122 lines (106 loc) · 3.82 KB
/
time.py
File metadata and controls
122 lines (106 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python3
'''
Name: Hamdy Abou El Anein
Email: hamdy.aea@protonmail.com
Date of creation: 17-11-2024
Last update: 17-11-2024
Version: 1.0
Description: The time command from GNU coreutils in Python3.
Example of use: python3 time.py sleep 2
'''
import os
import sys
import subprocess
import time
import resource
import argparse
def format_time(seconds):
"""Format elapsed time in hh:mm:ss or seconds for portability."""
if seconds >= 60:
return time.strftime("%H:%M:%S", time.gmtime(seconds))
return f"{seconds:.3f}s"
def collect_resource_usage():
"""Retrieve detailed resource usage statistics."""
usage = resource.getrusage(resource.RUSAGE_CHILDREN)
return {
"user_time": usage.ru_utime,
"sys_time": usage.ru_stime,
"max_resident_memory": usage.ru_maxrss,
"minor_page_faults": usage.ru_minflt,
"major_page_faults": usage.ru_majflt,
"voluntary_context_switches": usage.ru_nvcsw,
"involuntary_context_switches": usage.ru_nivcsw,
}
def run_command(args, output_file=None, append=False, portable=False):
"""Run a command and measure its execution time."""
start_time = time.time()
try:
result = subprocess.run(args, check=False)
exit_code = result.returncode
except FileNotFoundError:
print(f"Command not found: {args[0]}", file=sys.stderr)
sys.exit(127)
except PermissionError:
print(f"Permission denied: {args[0]}", file=sys.stderr)
sys.exit(126)
except Exception as e:
print(f"Error executing command: {e}", file=sys.stderr)
sys.exit(1)
end_time = time.time()
elapsed_time = end_time - start_time
usage = collect_resource_usage()
# Format output
if portable:
output = (
f"real {elapsed_time:.6f}\n"
f"user {usage['user_time']:.6f}\n"
f"sys {usage['sys_time']:.6f}\n"
)
else:
output = (
f"real {format_time(elapsed_time)}\n"
f"user {usage['user_time']:.3f}s\n"
f"sys {usage['sys_time']:.3f}s\n"
f"max_resident_memory {usage['max_resident_memory']} KB\n"
f"minor_page_faults {usage['minor_page_faults']}\n"
f"major_page_faults {usage['major_page_faults']}\n"
f"voluntary_context_switches {usage['voluntary_context_switches']}\n"
f"involuntary_context_switches {usage['involuntary_context_switches']}\n"
)
# Write output
if output_file:
mode = "a" if append else "w"
with open(output_file, mode) as f:
f.write(output)
else:
print(output, end="")
sys.exit(exit_code)
def main():
parser = argparse.ArgumentParser(
description="Run a command and measure its execution time and resource usage.",
add_help=False
)
parser.add_argument("command", nargs=argparse.REMAINDER, help="Command to execute.")
parser.add_argument("-p", "--portability", action="store_true", help="Use portable output format.")
parser.add_argument("-o", "--output", metavar="file", help="Write output to a file.")
parser.add_argument("-a", "--append", action="store_true", help="Append to the output file instead of overwriting.")
parser.add_argument("--help", action="store_true", help="Display help and exit.")
parser.add_argument("--version", action="store_true", help="Display version and exit.")
args = parser.parse_args()
if args.help:
parser.print_help()
sys.exit(0)
if args.version:
print("time.py 1.0")
sys.exit(0)
if not args.command:
print("time: missing command", file=sys.stderr)
sys.exit(1)
run_command(
args.command,
output_file=args.output,
append=args.append,
portable=args.portability
)
if __name__ == "__main__":
main()