fix: batch A - crash bugs, a leak, and startup/edge fragility
CI / compile (pull_request) Successful in 10s
CI / unit (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 10s
build / build (push) Failing after 46m40s
CI / compile (push) Successful in 20s
CI / unit (push) Successful in 19s
CI / integration (push) Successful in 16s
CI / compile (pull_request) Successful in 10s
CI / unit (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 10s
build / build (push) Failing after 46m40s
CI / compile (push) Successful in 20s
CI / unit (push) Successful in 19s
CI / integration (push) Successful in 16s
Seven confirmed defects from the code audit, each small and low-risk.
* ai_functions.get_random_cyclic_message: random.randint(0, len(CYCLIC_WORDS))
is inclusive -> could return len -> IndexError. Now randrange(len) + guard on
an empty CYCLIC_WORDS.
* librarian_commands.get_image_sadox: random.randrange(0, len(res)-1) never
picked the last comic and raised ValueError('empty range') on a single file.
Now randrange(len) + an empty-dir guard.
* ai_commands image generation: every DALL-E error branch replied but did not
return, so control fell through to `if response:` with response unbound ->
UnboundLocalError right after the friendly message. Each branch now returns;
response is pre-initialised; and PermissionDeniedError no longer passes a
(message, text) tuple as a single arg.
* search_bot DOI match: `item["DOI"] in data` was a substring test, so a DOI
that is a prefix of a longer one (10.1/1 vs 10.1/12) produced a false 'exists'
hit. Now matches the line's first whitespace token exactly, via an O(1) dict
index built once per consumer (also removes the O(queried-DOIs) per-line scan
- a real win for large databases).
* communication_subroutine.scan_incoming: matched records were never removed
from awaiting_q, so it grew unbounded over uptime and a reused UUID could
re-match a stale record. Matched records are now dropped after dispatch.
* communication_subroutine.id3: (resp.headers.get("icy-name") or "").title()
guards against a stream that omits headers (was AttributeError on None,
500-ing the /prepped_tracks "next" handler).
* betoniarka.scan_tracks: waits for the radio logs to exist instead of dying
with FileNotFoundError on a fresh deploy (which silently killed the
now-playing forwarder until a restart).
Verified: tests/unit/test_search_bot.py gains exact-match and trailing-metadata
cases; full unit job 43 passed. Remaining observations (image-gen stale
/home/pi fallback paths + dead FileNotFoundError-after-OSError branch; tailer
still vulnerable to mid-run log rotation; DOI-first-token assumption) noted for
follow-up - none are crashes on the normal path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #8.
This commit is contained in:
@@ -148,8 +148,11 @@ def consumer(in_q, control_q, doi, live_results, result_list, control_dict, expe
|
||||
print(f"Consumer thread started: {no} no")
|
||||
empty_counter = 0
|
||||
alive_no = 0
|
||||
# DOI -> result item, so a line is matched with one O(1) dict lookup instead
|
||||
# of scanning every queried DOI. Items are shared with result_list, so
|
||||
# setting exists here is seen by everyone.
|
||||
doi_index = {item["DOI"]: item for item in result_list}
|
||||
while True:
|
||||
done_check = True
|
||||
try:
|
||||
data = in_q.get(block=True, timeout = 1)
|
||||
if data is _sentinel:
|
||||
@@ -161,16 +164,21 @@ def consumer(in_q, control_q, doi, live_results, result_list, control_dict, expe
|
||||
alive_no += 1
|
||||
print(f"C{no}__{alive_no}\r", end="")
|
||||
|
||||
for item in result_list:
|
||||
if item["DOI"] in data and not item["exists"]:
|
||||
print(f"HIT in {no} content {data[0]} line {data[1]} file {data[2]} {item['exists']}")
|
||||
_logger.info(data)
|
||||
_logger.info("HIT")
|
||||
item["exists"] = True
|
||||
live_results.append(item)
|
||||
done_check = done_check and item["exists"]
|
||||
if done_check:
|
||||
control_q.put(_sentinel)
|
||||
# Each DB line is a DOI (optionally followed by metadata). Match
|
||||
# the WHOLE first token exactly - the old `item["DOI"] in data`
|
||||
# was a substring test, so a DOI that is a prefix of a longer one
|
||||
# (10.1/1 vs 10.1/12) produced a false 'exists' hit.
|
||||
parts = data.split()
|
||||
line_doi = parts[0] if parts else ""
|
||||
item = doi_index.get(line_doi)
|
||||
if item is not None and not item["exists"]:
|
||||
print(f"HIT in {no}: {line_doi}")
|
||||
_logger.info("HIT %s", line_doi)
|
||||
item["exists"] = True
|
||||
live_results.append(item)
|
||||
# All found? Signal producers to stop early (rare -> cheap).
|
||||
if all(it["exists"] for it in result_list):
|
||||
control_q.put(_sentinel)
|
||||
except Empty:
|
||||
empty_counter += 1
|
||||
time.sleep(1)
|
||||
|
||||
Reference in New Issue
Block a user