Skip to content

Commit a158db1

Browse files
authored
improved runner (#32)
Co-authored-by: rene-d <[email protected]>
1 parent e9b02e6 commit a158db1

File tree

1 file changed

+61
-38
lines changed

1 file changed

+61
-38
lines changed

scripts/runall.py

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def __iter__(self):
244244
if not k.startswith("_"):
245245
yield k, v
246246

247-
def _sql(self):
247+
def _columns_values(self):
248248
columns = list()
249249
values = list()
250250
for k, v in vars(self).items():
@@ -253,8 +253,11 @@ def _sql(self):
253253
values.append(v)
254254
return columns, values
255255

256+
def _values(self):
257+
return list(v for k, v in vars(self).items() if not k.startswith("_"))
258+
256259
def _select(self):
257-
columns, values = self._sql()
260+
columns, values = self._columns_values()
258261
sql = " and ".join(f"`{column}`=?" for column in columns)
259262
return sql, values
260263

@@ -346,24 +349,30 @@ def get_cache(cache_file: Path = None):
346349
return cache
347350

348351

349-
def check_cache(key: CacheKey, file_timestamp: Path, table: str, columns: t.Iterable[str], no_age_check=False):
352+
def check_cache(
353+
key: CacheKey, timestamp_file: Path, table: str, columns: t.Iterable[str], no_age_check=False, having=None
354+
):
350355
cache = get_cache()
351356
db = cache["db"]
352357
db.row_factory = sqlite3.Row
353358

354-
where, key_values = key._select()
355-
cursor = db.execute(f"select * from `{table}` where {where}", key_values)
359+
key_columns, key_values = key._columns_values()
360+
where = " and ".join(f"`{column}`=?" for column in key_columns)
361+
sql = f"select * from `{table}` where {where}"
362+
363+
if having:
364+
sql += " group by " + ",".join(f"`{column}`" for column in key_columns)
365+
sql += " having " + having
366+
367+
cursor = db.execute(sql, key_values)
368+
356369
row = cursor.fetchone()
357370
if row:
358-
timestamp = file_timestamp.stat().st_mtime_ns
371+
timestamp = timestamp_file.stat().st_mtime_ns
359372
if row["mtime_ns"] == timestamp or no_age_check:
360373
return dict((column, row[column]) for column in columns)
361374

362375
else:
363-
# seconds = round((timestamp - e["timestamp"]) / 1000000000)
364-
# delta = timedelta(seconds=seconds)
365-
# print(f"{FEINT}{ITALIC}entry {key} is out of date for {delta}{RESET}", end=f"{CR}")
366-
367376
print_log(f"{FEINT}{ITALIC}entry {key} is out of date{RESET}", end=TRANSIENT)
368377

369378
else:
@@ -704,11 +713,12 @@ def run_day(
704713
wait: float,
705714
quiet: bool,
706715
):
707-
elapsed = defaultdict(list)
716+
elapsed_by_lang = defaultdict(list)
708717

709718
day_suffix = mday.removeprefix(str(day))
710719
name_max_len = 16 - len(day_suffix)
711720

721+
# for puzzle inputs
712722
for crc, file in sorted(day_inputs.items(), key=itemgetter(1)):
713723
input_name = file.parent.parent.name
714724
input_name = input_name.removeprefix("tmp-")[:16]
@@ -722,44 +732,57 @@ def run_day(
722732
else:
723733
prefix = f"{CYAN}{prefix}{RESET}"
724734

725-
results = set()
735+
# set to check if all answers are identical
736+
# answers_set = set()
726737

738+
# for all available languages
727739
for lang, (pattern, interpreter) in languages.items():
728740
prog = Path(pattern.format(year=year, day=mday, AOC_TARGET_DIR=Env.AOC_TARGET_DIR))
729-
key = SolutionKey(year, day, crc, str(prog), lang.lower())
730-
731-
if not prog.is_file():
732-
# special case for day13_alt/day13.py
733-
if "_" in prog.stem and prog.stem == prog.parent.name:
734-
prog = prog.with_stem(prog.stem[: prog.stem.find("_")])
735-
736741
if not prog.is_file():
737742
continue
738743

744+
key = SolutionKey(year, day, crc, str(prog), lang.lower())
745+
739746
if prune:
740747
prune_cache(key, "solutions")
741748
continue
742749

743-
if refresh:
744-
e = None
745-
in_cache = False
746-
else:
747-
e = check_cache(key, prog, "solutions", ("elapsed", "status", "answers"), dry_run)
748-
in_cache = e is not None
750+
e = check_cache(key, prog, "solutions", ("elapsed", "status", "answers"), no_age_check=dry_run)
751+
in_cache = e is not None
749752

750-
if not in_cache and not dry_run:
753+
timing_status = "☽" if in_cache else " "
754+
755+
if (not in_cache and not dry_run) or refresh:
751756
nb_expected = 1 if day == 25 else 2
752757

758+
cached_e = e
759+
753760
e = run(prog, lang, interpreter, file, day_answers.get(crc), nb_expected, year, day, quiet)
754761

755762
if e:
756-
update_cache(key, prog, "solutions", e)
763+
timing = e["elapsed"]
764+
765+
# if a valid solution exists in the database
766+
if cached_e and cached_e["status"] in ("ok", "unknown"):
767+
# if the solution is valid and faster than the cached one, update it
768+
if e["status"] in ("ok", "unknown") and cached_e["elapsed"] > timing:
769+
update_cache(key, prog, "solutions", e)
770+
timing_status = "☀️"
771+
else:
772+
# otyherwire, the timing for the solution is the cached one
773+
timing = cached_e["elapsed"]
774+
else:
775+
# no cached solution or invalid solution in the cache
776+
update_cache(key, prog, "solutions", e)
757777

758778
if wait is not None:
759779
if not quiet:
760780
print_log(f"{CR}{CLEAR_EOL}waiting {wait:g}s...", end="")
761781
time.sleep(wait)
762782

783+
else:
784+
timing = e["elapsed"]
785+
763786
if not e:
764787
continue
765788

@@ -790,7 +813,7 @@ def run_day(
790813
f" {YELLOW}{lang:<7}{RESET}:"
791814
f" {status_color}{e['status']:7}{RESET}"
792815
f" {WHITE}{e['elapsed'] / 1e9:7.3f}s"
793-
f" {GRAY}{'☽' if in_cache else ' '}"
816+
f" {GRAY}{timing_status}"
794817
)
795818

796819
if quiet:
@@ -805,20 +828,23 @@ def run_day(
805828
if e["status"] in ("error", "failed"):
806829
problems.append(line)
807830

808-
results.add(answers)
831+
# answers_set.add(answers)
809832

810-
elapsed[lang].append(e["elapsed"] / 1e9)
833+
elapsed_by_lang[lang].append(timing / 1e9)
811834

812-
# if len(results) > 1:
835+
# if len(answers_set) > 1:
813836
# line = f"{prefix} {RED}{BLINK}MISMATCH BETWEEN SOLUTIONS{RESET}"
814837
# print(line)
815838
# problems.append(line)
816839

817-
nb_samples = set(len(t) for _, t in elapsed.items())
818-
assert len(nb_samples) == 1 or len(nb_samples) == 0
819-
nb_samples = 0 if len(nb_samples) == 0 else nb_samples.pop()
840+
samples_set = set(len(i) for i in elapsed_by_lang.values())
841+
if not dry_run:
842+
# if not dry run, all languages should have the same puzzle input count
843+
# if dry run, some languages may have not be run
844+
assert len(samples_set) == 1 or len(samples_set) == 0
845+
nb_samples = 0 if len(samples_set) == 0 else max(samples_set)
820846

821-
return dict((lang, sum(t) / len(t)) for lang, t in elapsed.items()), nb_samples
847+
return dict((lang, sum(t) / len(t)) for lang, t in elapsed_by_lang.items()), nb_samples
822848

823849

824850
def get_languages(filter_lang: t.Iterable[str]) -> t.Dict[str, t.Tuple[str, t.Union[str, None]]]:
@@ -1146,9 +1172,6 @@ def main():
11461172
except KeyboardInterrupt:
11471173
pass
11481174

1149-
# except Exception as e:
1150-
# print(f"{RED}ERROR {e}{RESET}")
1151-
11521175
finally:
11531176
if stats_elapsed:
11541177
languages = sorted(set(map(itemgetter(3), stats_elapsed.keys())))

0 commit comments

Comments
 (0)