Audit round 2: aspect measured the case too; defensive image decode; own-key state reset
4. aspect_profile had the same root cause as edges/centering but no guard. On the cased photo it reported 5.8% off standard -- the CASE's proportions -- and the UI would surface that as a trimming hint about a card it never measured. This one is more directly load-bearing than the other two, since the measurement IS the detected box. Now refuses, with both consumers (prompt block and UI hint) checking reliable. 5. _decode_images could KeyError/raise out of a plain read on a malformed row, and separately b64decode returns b'' rather than raising on some corrupt input -- which would have fed a zero-byte 'photo' into grading to fail confusingly deep in the pipeline. Both now degrade to the existing re-pick path. 6. _last_used_own_key is instance state on a handler that serves every request on a keep-alive connection, so it outlives the request that set it. Currently safe (every logging path assigns first), but a future path that logged without reaching the assignment would bill the previous request's payer. Cleared up-front now. Full regression suite re-run: die-cut exclusion, uniform cards, centering, aspect, cased-photo refusals, and the store layer all still behave.
This commit is contained in:
parent
6c612c1f4a
commit
0cbeb26665
5 changed files with 39 additions and 4 deletions
21
store.py
21
store.py
|
|
@ -6,6 +6,7 @@ it told you, so you can look back at a card without re-running the estimate.
|
|||
"""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
|
|
@ -202,8 +203,24 @@ def _decode_images(raw):
|
|||
items = json.loads(raw)
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
return [(base64.standard_b64decode(item["image_base64"]), item.get("filename") or "upload")
|
||||
for item in items]
|
||||
# Decoding is inside the guard too, not just the JSON parse: a row
|
||||
# written by an older/newer shape, or truncated, would otherwise raise
|
||||
# KeyError/binascii.Error out of a plain read and 500 the request.
|
||||
# Falling back to "no stored photos" degrades to the re-pick path,
|
||||
# which already exists and is the right outcome here.
|
||||
try:
|
||||
decoded = [(base64.standard_b64decode(item["image_base64"]),
|
||||
item.get("filename") or "upload")
|
||||
for item in items]
|
||||
except (KeyError, TypeError, ValueError, binascii.Error):
|
||||
return None
|
||||
# b64decode doesn't raise on some malformed input, it just returns b''
|
||||
# — so a corrupt row would otherwise hand grading a zero-byte "photo"
|
||||
# and fail confusingly deep in the pipeline. Treat any empty entry as
|
||||
# the row being unusable and fall back to asking for the photo again.
|
||||
if not decoded or any(not b for b, _ in decoded):
|
||||
return None
|
||||
return decoded
|
||||
|
||||
|
||||
def save_grade(grade, thumbnail=None, label=None, source_images=None):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue