11"""Run every solution."""
22
33import subprocess
4- from collections .abc import Callable , Mapping , MutableMapping
4+ from collections .abc import Mapping , MutableMapping
55from pathlib import Path
66from typing import Final
77
8- type Triple = tuple [int , float , str ]
9- type RunnerFunc = Callable [[Path ], Triple ]
10-
11-
12- def execute_command (command : list [str | Path ]) -> Triple :
13- # Here rather than globally as it's easier to patch.
14- print ("Running" , command )
15- result = subprocess .run (
16- ["/usr/bin/time" , "-f" , "%M,%S,%U" ] + command , capture_output = True , timeout = 60 , text = True , check = True
17- )
18- # todo
19- # kilobytes, sys_seconds, user_seconds = result.stderr.split("\n")[-1].split(",")
20- kilobytes , sys_seconds , user_seconds = result .stderr .split ("," )
21- return int (kilobytes ), float (sys_seconds ) + float (user_seconds ), result .stdout
22-
23-
24- def run_python (dirpath : Path ) -> Triple :
25- """Run a Python solution."""
26- return execute_command (["python" , dirpath / "solution.py" ])
27-
28-
29- def run_racket (dirpath : Path ) -> Triple :
30- """Run a Racket solution."""
31- return execute_command (["racket" , dirpath / "solution.rkt" ])
32-
33-
34- def run_rust (dirpath : Path ) -> Triple :
35- return execute_command (["cargo" , "run" , "--quiet" , "--manifest-path" , dirpath / "Cargo.toml" ])
36-
37-
38- def run_fsharp (dirpath : Path ) -> Triple :
39- return execute_command (["dotnet" , "fsi" , dirpath / "solution.fsx" ])
40-
41-
42- def run_ocaml (dirpath : Path ) -> Triple :
43- return execute_command (["ocaml" , dirpath / "solution.ml" ])
44-
45-
46- def run_jupyter (dirpath : Path ) -> Triple :
47- return execute_command (["ipython" , "-c" , f"%run { dirpath / 'solution.ipynb' } " ])
48-
8+ from advent_of_action import runners
9+ from advent_of_action .runners import RunnerFunc
4910
5011# Languages and their commands
5112RUNTIMES : Final [dict [str , RunnerFunc ]] = {
52- "python" : run_python ,
53- "racket" : run_racket ,
54- "rust" : run_rust ,
55- "fsharp" : run_fsharp ,
56- "ocaml" : run_ocaml ,
57- "jupyter" : run_jupyter ,
13+ "python" : runners . python ,
14+ "racket" : runners . racket ,
15+ "rust" : runners . rust ,
16+ "fsharp" : runners . fsharp ,
17+ "ocaml" : runners . ocaml ,
18+ "jupyter" : runners . jupyter ,
5819}
5920
6021type Day = str
@@ -74,27 +35,48 @@ def measure_execution_time(dirpath: Path, ext: RunnerFunc) -> Stats:
7435 return "" , "" , "Different answer"
7536 except subprocess .CalledProcessError as e :
7637 return "" , "" , f"Error ({ e .returncode } )"
77- return f"{ seconds :.2f} sec" , f"{ kilobytes } KB" , ""
38+ return f"{ seconds :.2f} " , f"{ kilobytes } " , ""
39+
40+
41+ def from_table (table : str ) -> dict [Run , Stats ]:
42+ results : dict [Run , Stats ] = {}
43+ for line in table .split ("\n " )[6 :]:
44+ if not line :
45+ break
46+ day , lang , person , seconds , kb , notes = line [1 :- 1 ].split (" | " )
47+ results [(day .strip (), lang .strip (), person .strip ())] = (
48+ seconds .strip (),
49+ kb .strip (),
50+ notes .strip (),
51+ )
52+ return results
53+
54+
55+ def to_table (results : Mapping [Run , Stats ]) -> str :
56+ table = "\n \n ## Stats\n \n "
57+ table += "| day | language | who | time (s) | mem (KB) | notes |\n "
58+ table += "| --- | --- | --- | --- | --- | --- |\n "
59+ for (day , language , person ), (seconds , kilobytes , notes ) in results .items ():
60+ table += f"| { day } | { language } | { person } | { seconds } | { kilobytes } | { notes } |\n "
61+ return table
7862
7963
8064def write_results (the_results : Mapping [Run , Stats ]) -> None :
81- readme_path = "README.md"
82- new_content = "\n ## Results\n \n "
83- new_content += "| day | language | who | time | mem | notes |\n "
84- new_content += "| --- | --- | --- | --- | --- | --- |\n "
85- for (day , language , person ), (seconds , kilobytes , notes ) in the_results .items ():
86- new_content += f"| { day } | { language } | { person } | { seconds } | { kilobytes } | { notes } |\n "
87-
88- old_content = ""
89- if Path (readme_path ).exists ():
90- with open (readme_path ) as f :
91- lines = f .readlines ()
92- for line in lines :
93- if line .strip () == "## Results" :
94- break
95- old_content += line
96- with open (readme_path , "w" ) as f :
97- f .write (old_content + new_content )
65+ readme = Path ("README.md" )
66+ old_content = readme .read_text ()
67+ section_begins = old_content .find ("\n \n ## Stats" )
68+ if section_begins > - 1 :
69+ section_ends = old_content .find ("\n \n ##" , section_begins + 1 )
70+ section = old_content [section_begins :section_ends ] if section_ends else old_content [section_begins :]
71+ old_dict = from_table (section )
72+ the_results = {** old_dict , ** the_results }
73+ if section_ends > - 1 :
74+ new_content = old_content [:section_begins ] + to_table (the_results ) + old_content [section_ends :]
75+ else :
76+ new_content = old_content [:section_begins ] + to_table (the_results )
77+ else :
78+ new_content = old_content + to_table (the_results )
79+ readme .write_text (new_content )
9880
9981
10082def main () -> None :
0 commit comments