diff --git a/docs/reviews/2026-08-23-code-review.md b/docs/reviews/2026-08-23-code-review.md index 79e4804..1da10e5 100644 --- a/docs/reviews/2026-08-23-code-review.md +++ b/docs/reviews/2026-08-23-code-review.md @@ -384,6 +384,7 @@ The critical gap is transaction atomicity (HIGH-04) — the audit verdict is **" ```python # src/transcription/services/base.py + @asynccontextmanager async def unit_of_work( services: ServiceBundle, @@ -396,10 +397,12 @@ async def unit_of_work( HIGH-01 structurally hard to reintroduce. """ + async def run_blocking[T](fn: Callable[[], T]) -> T: """Run a CPU- or disk-bound callable off the event loop.""" return await asyncio.to_thread(fn) + async def insert_with_sequence_retry( session: AsyncSession, *, diff --git a/src/transcription/config.py b/src/transcription/config.py index edfd334..095584a 100644 --- a/src/transcription/config.py +++ b/src/transcription/config.py @@ -207,7 +207,7 @@ LOGGING_CONFIG: dict[str, Any] = { "maxBytes": 10 * 1024 * 1024, "backupCount": 5, "encoding": "utf-8", - } + }, }, "root": { "level": "INFO", diff --git a/src/transcription/db/migration.py b/src/transcription/db/migration.py index 1778dc5..8760a81 100644 --- a/src/transcription/db/migration.py +++ b/src/transcription/db/migration.py @@ -93,11 +93,15 @@ def export_bundle(*, source_db_url: str, source_upload_dir: Path, bundle_dir: Pa if legacy_column not in export_columns: export_columns.append(legacy_column) if table_name == "person" and "portrait_path" in source_table.columns: - legacy_portrait_rows = connection.execute( - select(source_table.c["id"], source_table.c["portrait_path"]).where( - source_table.c["portrait_path"].is_not(None) + legacy_portrait_rows = ( + connection.execute( + select(source_table.c["id"], source_table.c["portrait_path"]).where( + source_table.c["portrait_path"].is_not(None) + ) ) - ).mappings().all() + .mappings() + .all() + ) rows = connection.execute(select(*(source_table.c[name] for name in export_columns))).mappings().all() payload["tables"][table_name] = [ _serialize_row(row, table_name=table_name, source_upload_dir=source_upload_dir) for row in rows @@ -344,15 +348,9 @@ def _prepare_photo_payload_and_uploads( # noqa: PLR0915 photo_rows[:] = retained_rows existing_homepage_rows = [row for row in photo_rows if row.get("person_id") is None] - existing_person_ids = { - str(row["person_id"]) - for row in photo_rows - if row.get("person_id") is not None - } + existing_person_ids = {str(row["person_id"]) for row in photo_rows if row.get("person_id") is not None} existing_primary_person_ids = { - str(row["person_id"]) - for row in photo_rows - if row.get("person_id") is not None and bool(row.get("is_primary")) + str(row["person_id"]) for row in photo_rows if row.get("person_id") is not None and bool(row.get("is_primary")) } has_homepage_primary = any(bool(row.get("is_primary")) for row in existing_homepage_rows) diff --git a/src/transcription/db/models.py b/src/transcription/db/models.py index c48d7f0..527691a 100644 --- a/src/transcription/db/models.py +++ b/src/transcription/db/models.py @@ -435,9 +435,7 @@ class Source(SQLModel, table=True): """ job_sources = _loaded_attribute(self, "job_sources") or () dated = [ - (job, job_source) - for job_source in job_sources - if (job := _loaded_attribute(job_source, "job")) is not None + (job, job_source) for job_source in job_sources if (job := _loaded_attribute(job_source, "job")) is not None ] if dated: return max(dated, key=lambda pair: pair[0].date_created)[1] diff --git a/src/transcription/db/runtime.py b/src/transcription/db/runtime.py index 3557e52..aaf163f 100644 --- a/src/transcription/db/runtime.py +++ b/src/transcription/db/runtime.py @@ -50,8 +50,7 @@ def initialize_database_runtime(*, settings: Settings | None = None) -> Database runtime_url = runtime.engine.url.render_as_string(hide_password=False) if runtime_url != database_url: raise RuntimeError( - "Database runtime is already initialized for a different database: " - f"{runtime_url!r} != {database_url!r}" + f"Database runtime is already initialized for a different database: {runtime_url!r} != {database_url!r}" ) return runtime diff --git a/src/transcription/services/documents.py b/src/transcription/services/documents.py index ef5dd29..33649f4 100644 --- a/src/transcription/services/documents.py +++ b/src/transcription/services/documents.py @@ -582,8 +582,7 @@ class DocumentService(ServiceBase): ) existing_tags = ( - (await _session.exec(select(Tag).where(col(Tag.normalized_label).in_(label_keys)))) - .all() + (await _session.exec(select(Tag).where(col(Tag.normalized_label).in_(label_keys)))).all() if label_keys else [] ) @@ -598,9 +597,7 @@ class DocumentService(ServiceBase): tags_by_key[key] = tag selected_tag_ids.add(tag.id) - links = ( - await _session.exec(select(DocumentTag).where(DocumentTag.document_id == document_id)) - ).all() + links = (await _session.exec(select(DocumentTag).where(DocumentTag.document_id == document_id))).all() existing_ids = {link.tag_id for link in links} for link in links: diff --git a/src/transcription/services/jobs.py b/src/transcription/services/jobs.py index 6dd8607..ca6079d 100644 --- a/src/transcription/services/jobs.py +++ b/src/transcription/services/jobs.py @@ -278,9 +278,7 @@ class JobService(ServiceBase): ) attempt_count = ( await _session.exec( - select(func.count()) - .select_from(ExecutionAttempt) - .where(ExecutionAttempt.job_id == job_id) + select(func.count()).select_from(ExecutionAttempt).where(ExecutionAttempt.job_id == job_id) ) ).one() if attempt_count: @@ -320,11 +318,7 @@ class JobService(ServiceBase): ) attempts = list( - ( - await session.exec( - select(ExecutionAttempt).where(ExecutionAttempt.job_id == job_id) - ) - ).all() + (await session.exec(select(ExecutionAttempt).where(ExecutionAttempt.job_id == job_id))).all() ) for attempt in attempts: await session.delete(attempt) diff --git a/src/transcription/services/media_storage.py b/src/transcription/services/media_storage.py index 3939eb1..fe4b788 100644 --- a/src/transcription/services/media_storage.py +++ b/src/transcription/services/media_storage.py @@ -67,9 +67,13 @@ async def persist_named_media( ) -> Path: """Resolve a target directory/name and persist media bytes safely.""" target_dir = root if namespace is None else root / Path(namespace) - stored_name = Path(filename).name if preserve_original_name else build_stored_filename( - filename=filename, - filename_stem=filename_stem, + stored_name = ( + Path(filename).name + if preserve_original_name + else build_stored_filename( + filename=filename, + filename_stem=filename_stem, + ) ) return await write_media_bytes( target_dir=target_dir, diff --git a/src/transcription/services/people.py b/src/transcription/services/people.py index 4ff4431..6acc82f 100644 --- a/src/transcription/services/people.py +++ b/src/transcription/services/people.py @@ -277,8 +277,7 @@ class PeopleService(ServiceBase): ) existing_tags = ( - (await _session.exec(select(Tag).where(col(Tag.normalized_label).in_(label_keys)))) - .all() + (await _session.exec(select(Tag).where(col(Tag.normalized_label).in_(label_keys)))).all() if label_keys else [] ) diff --git a/src/transcription/services/quality.py b/src/transcription/services/quality.py index bb9f0ea..ee618a5 100644 --- a/src/transcription/services/quality.py +++ b/src/transcription/services/quality.py @@ -65,9 +65,7 @@ def analyze_transcription_quality(text: str) -> tuple[QualityWarning, ...]: warnings.append( QualityWarning( code=QualityWarningCode.REDUNDANT_HANDWRITING_WRAPPERS, - detail=( - "A wholly handwritten document also uses repeated whole-line handwriting wrappers." - ), + detail=("A wholly handwritten document also uses repeated whole-line handwriting wrappers."), ) ) diff --git a/src/transcription/services/registry.py b/src/transcription/services/registry.py index 8079d90..796b79b 100644 --- a/src/transcription/services/registry.py +++ b/src/transcription/services/registry.py @@ -249,8 +249,7 @@ class RegistryService[ModelT: RegistryEntry](ServiceBase): f"Built-in {self.noun} {entry.label!r} cannot be deleted", category=ErrorCategory.CONFLICT, suggestion=( - f"Deactivate the {self.short_noun} instead; " - "its built-in meaning must remain available." + f"Deactivate the {self.short_noun} instead; its built-in meaning must remain available." ), ) if await self._is_referenced(session=_session, entry=entry): @@ -258,8 +257,7 @@ class RegistryService[ModelT: RegistryEntry](ServiceBase): f"{self.noun} {entry.label!r} is referenced and cannot be deleted", category=ErrorCategory.CONFLICT, suggestion=( - f"Deactivate the {self.short_noun} instead; " - f"{self.referenced_retainer} will retain it." + f"Deactivate the {self.short_noun} instead; {self.referenced_retainer} will retain it." ), ) await _session.delete(entry) diff --git a/src/transcription/services/sources.py b/src/transcription/services/sources.py index 9b909ad..93e895c 100644 --- a/src/transcription/services/sources.py +++ b/src/transcription/services/sources.py @@ -351,9 +351,7 @@ class SourceService(ServiceBase): async with self._session_scope(session) as _session: job_source = ( await _session.exec( - select(JobSource) - .where(JobSource.job_id == job_id) - .where(JobSource.source_id == source_id) + select(JobSource).where(JobSource.job_id == job_id).where(JobSource.source_id == source_id) ) ).first() if job_source is None: @@ -400,9 +398,7 @@ class SourceService(ServiceBase): linked_job_sources = list(source.job_sources) attempt_count = ( await _session.exec( - select(func.count()) - .select_from(ExecutionAttempt) - .where(ExecutionAttempt.source_id == source_id) + select(func.count()).select_from(ExecutionAttempt).where(ExecutionAttempt.source_id == source_id) ) ).one() if attempt_count: @@ -586,11 +582,7 @@ class SourceService(ServiceBase): _session.add(attempt) await _session.flush() - if ( - text is not None - and source.raw_transcription is None - and source.preferred_execution_attempt_id is None - ): + if text is not None and source.raw_transcription is None and source.preferred_execution_attempt_id is None: source.raw_transcription = text source.preferred_execution_attempt_id = attempt.id diff --git a/src/transcription/ui/components/upload_panel.py b/src/transcription/ui/components/upload_panel.py index d27d02e..fd0c25c 100644 --- a/src/transcription/ui/components/upload_panel.py +++ b/src/transcription/ui/components/upload_panel.py @@ -43,8 +43,4 @@ def render_upload_picker( props.append("webkitdirectory directory") if multiple: props.append("multiple") - return ( - ui.upload(on_upload=on_upload, auto_upload=True, label=label) - .props(" ".join(props)) - .classes("w-full") - ) + return ui.upload(on_upload=on_upload, auto_upload=True, label=label).props(" ".join(props)).classes("w-full") diff --git a/src/transcription/ui/pages/documents_page.py b/src/transcription/ui/pages/documents_page.py index 6d211c8..e4d978c 100644 --- a/src/transcription/ui/pages/documents_page.py +++ b/src/transcription/ui/pages/documents_page.py @@ -377,9 +377,7 @@ def register_page() -> None: # noqa: PLR0915 if document.sources or document.jobs: render_delete_blocked_notice( reason="Delete is blocked because related records exist.", - detail=dependency_summary( - [("Sources", bool(document.sources)), ("Jobs", bool(document.jobs))] - ), + detail=dependency_summary([("Sources", bool(document.sources)), ("Jobs", bool(document.jobs))]), guidance="Remove related records first, then retry deletion.", back_label="Back to Document", back_target=f"/documents/{document.id}", @@ -497,14 +495,18 @@ def _render_document_form_fields( if document is not None else [] ) - tags_input = ui.select( - sorted(tag_options, key=str.casefold), - label="Tags", - value=selected_tags, - multiple=True, - with_input=True, - new_value_mode="add-unique", - ).props("outlined use-chips").classes("w-full ui-form-surface") + tags_input = ( + ui.select( + sorted(tag_options, key=str.casefold), + label="Tags", + value=selected_tags, + multiple=True, + with_input=True, + new_value_mode="add-unique", + ) + .props("outlined use-chips") + .classes("w-full ui-form-surface") + ) linked_people.render() diff --git a/src/transcription/ui/pages/home_page.py b/src/transcription/ui/pages/home_page.py index a96c51b..da42477 100644 --- a/src/transcription/ui/pages/home_page.py +++ b/src/transcription/ui/pages/home_page.py @@ -88,6 +88,7 @@ def _render_homepage_gallery( ui.label(f"{active_index[0] + 1} of {len(photos)}").classes("text-xs ui-text-muted") if enable_rotation and rotate_enabled is not None: + def set_rotation(enabled: bool) -> None: rotate_enabled[0] = enabled if on_change is not None: @@ -128,10 +129,14 @@ def _render_homepage_editor(*, render_image_panel, markdown_input, on_upload) -> render_image_panel() with ui.column().classes("col-span-12 lg:col-span-5 gap-4"), archival_card(title="Home Text"): - markdown_input[0] = ui.textarea( - label="Homepage markdown", - value=read_homepage_markdown(), - ).props("outlined autogrow").classes("w-full") + markdown_input[0] = ( + ui.textarea( + label="Homepage markdown", + value=read_homepage_markdown(), + ) + .props("outlined autogrow") + .classes("w-full") + ) with ui.column().classes("col-span-12 lg:col-span-3"): ui.element("div") @@ -198,10 +203,14 @@ def register_page() -> None: # noqa: PLR0915 if photos: current_photo = photos[active_index[0]] - description_input = ui.input( - label="Image description", - value=current_photo.description or "", - ).props("outlined dense").classes("w-full") + description_input = ( + ui.input( + label="Image description", + value=current_photo.description or "", + ) + .props("outlined dense") + .classes("w-full") + ) async def save_description() -> None: try: diff --git a/src/transcription/ui/pages/jobs_page.py b/src/transcription/ui/pages/jobs_page.py index f37d8be..5daf13d 100644 --- a/src/transcription/ui/pages/jobs_page.py +++ b/src/transcription/ui/pages/jobs_page.py @@ -361,9 +361,7 @@ def register_page() -> None: # noqa: PLR0915 return resubmittable_count = sum( - 1 - for js in job.job_sources - if js.status in {JobSourceStatus.FAILED, JobSourceStatus.CANCELLED} + 1 for js in job.job_sources if js.status in {JobSourceStatus.FAILED, JobSourceStatus.CANCELLED} ) with ui.column().classes("w-full max-w-xl mx-auto p-4 gap-4"): @@ -374,8 +372,7 @@ def register_page() -> None: # noqa: PLR0915 metadata_row("Current Status:", job.status.value.upper()) metadata_row("Resubmittable Sources:", str(resubmittable_count)) ui.label( - "Resubmit queues failed and cancelled linked sources. " - "Prior execution evidence remains preserved." + "Resubmit queues failed and cancelled linked sources. Prior execution evidence remains preserved." ).classes("text-xs ui-text-muted") async def submit_resubmit() -> None: @@ -440,9 +437,7 @@ def register_page() -> None: # noqa: PLR0915 ui.label( "Related JobSource links, execution attempts, transport responses, and attempt artifacts " "will be removed. Source records and files remain until deleted separately." - ).classes( - "text-xs ui-text-muted" - ) + ).classes("text-xs ui-text-muted") async def submit_delete() -> None: try: diff --git a/src/transcription/ui/pages/people_page.py b/src/transcription/ui/pages/people_page.py index 577078b..625a191 100644 --- a/src/transcription/ui/pages/people_page.py +++ b/src/transcription/ui/pages/people_page.py @@ -368,9 +368,9 @@ def register_page() -> None: # noqa: PLR0915 label="", on_upload=on_photo_selected, auto_upload=True, - ).props( - f'multiple accept="{",".join(sorted(IMAGE_UPLOAD_EXTENSIONS))}"' - ).classes("hidden person-photo-upload") + ).props(f'multiple accept="{",".join(sorted(IMAGE_UPLOAD_EXTENSIONS))}"').classes( + "hidden person-photo-upload" + ) ui.button( "Upload Photo(s)", on_click=lambda: ui.run_javascript( @@ -606,14 +606,18 @@ def _render_person_form_fields( if person is not None else [] ) - tags_input = ui.select( - sorted(tag_options, key=str.casefold), - label="Tags", - value=selected_tags, - multiple=True, - with_input=True, - new_value_mode="add-unique", - ).props("outlined use-chips").classes("w-full ui-form-surface") + tags_input = ( + ui.select( + sorted(tag_options, key=str.casefold), + label="Tags", + value=selected_tags, + multiple=True, + with_input=True, + new_value_mode="add-unique", + ) + .props("outlined use-chips") + .classes("w-full ui-form-surface") + ) return PersonFormFields( last_name=last_name_input, diff --git a/src/transcription/ui/pages/sources_page.py b/src/transcription/ui/pages/sources_page.py index ca33cc3..1d0f685 100644 --- a/src/transcription/ui/pages/sources_page.py +++ b/src/transcription/ui/pages/sources_page.py @@ -651,19 +651,11 @@ def _render_machine_candidates( evidence_service: EvidenceService, ) -> None: successful = [ - attempt - for attempt in attempts - if attempt.status == JobSourceStatus.TRANSCRIBED and attempt.raw_transcription - ] - candidates = [ - attempt for attempt in successful if attempt.id != source.preferred_execution_attempt_id + attempt for attempt in attempts if attempt.status == JobSourceStatus.TRANSCRIBED and attempt.raw_transcription ] + candidates = [attempt for attempt in successful if attempt.id != source.preferred_execution_attempt_id] preferred_attempt = next( - ( - attempt - for attempt in successful - if attempt.id == source.preferred_execution_attempt_id - ), + (attempt for attempt in successful if attempt.id == source.preferred_execution_attempt_id), None, ) @@ -697,9 +689,7 @@ def _render_machine_candidates( with ui.grid().classes("w-full grid-cols-1 md:grid-cols-2 gap-3"): with ui.column().classes("gap-1"): ui.label("Preferred machine transcription").classes("text-xs font-semibold") - ui.label(source.raw_transcription).classes( - "p-2 ui-note-box text-xs whitespace-pre-wrap" - ) + ui.label(source.raw_transcription).classes("p-2 ui-note-box text-xs whitespace-pre-wrap") with ui.column().classes("gap-1"): ui.label("Candidate transcription").classes("text-xs font-semibold") ui.label(attempt.raw_transcription or "").classes( diff --git a/tests/integration/test_pipeline_flow.py b/tests/integration/test_pipeline_flow.py index f2acaf3..d13faae 100644 --- a/tests/integration/test_pipeline_flow.py +++ b/tests/integration/test_pipeline_flow.py @@ -26,9 +26,7 @@ from transcription.services.workflows import advance_job async def _attempts_for_job(session, job) -> list[ExecutionAttempt]: """Load execution attempts for a job; V4.7 moved evidence off JobSource.""" job_source_ids = [job_source.id for job_source in job.job_sources] - result = await session.exec( - select(ExecutionAttempt).where(col(ExecutionAttempt.job_source_id).in_(job_source_ids)) - ) + result = await session.exec(select(ExecutionAttempt).where(col(ExecutionAttempt.job_source_id).in_(job_source_ids))) return list(result.all()) diff --git a/tests/services/test_job_service.py b/tests/services/test_job_service.py index d8a76f6..a34a0d7 100644 --- a/tests/services/test_job_service.py +++ b/tests/services/test_job_service.py @@ -154,11 +154,7 @@ class TestJobService: finally: event.remove(bind, "before_cursor_execute", capture) - claim_sql = [ - item - for item in statements - if item.lstrip().upper().startswith(("SELECT", "UPDATE")) - ] + claim_sql = [item for item in statements if item.lstrip().upper().startswith(("SELECT", "UPDATE"))] assert len(claim_sql) == 1, claim_sql assert "LIMIT" in claim_sql[0].upper() assert "JOIN" not in claim_sql[0].upper() diff --git a/tests/services/test_photo_service.py b/tests/services/test_photo_service.py index 7ad5e33..57f1802 100644 --- a/tests/services/test_photo_service.py +++ b/tests/services/test_photo_service.py @@ -7,10 +7,13 @@ from transcription.db.models import Person from transcription.services.people import PeopleService from transcription.services.photos import PhotosService -PNG_BYTES = bytes.fromhex( - "89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000a49444154789c6360000002000100" - "05fe02fea7b1b8000000004945" -) + b"NDAE\xae\x42\x60\x82" +PNG_BYTES = ( + bytes.fromhex( + "89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000a49444154789c6360000002000100" + "05fe02fea7b1b8000000004945" + ) + + b"NDAE\xae\x42\x60\x82" +) @pytest.mark.asyncio diff --git a/tests/services/test_quality.py b/tests/services/test_quality.py index fbd0a77..89035e6 100644 --- a/tests/services/test_quality.py +++ b/tests/services/test_quality.py @@ -35,9 +35,7 @@ def test_quality_analysis_accepts_clean_transcription(): @pytest.mark.unit def test_quality_warning_payload_is_versioned(): - payload = quality_warning_payload( - analyze_transcription_quality("[document body typeset]\nBroken \ufffd") - ) + payload = quality_warning_payload(analyze_transcription_quality("[document body typeset]\nBroken \ufffd")) assert payload["schema_name"] == "transcription.quality-warnings" assert payload["schema_version"] == "1" diff --git a/tests/services/test_store.py b/tests/services/test_store.py index efde589..13783e4 100644 --- a/tests/services/test_store.py +++ b/tests/services/test_store.py @@ -73,10 +73,7 @@ async def test_create_job_for_document_sorts_sources_and_creates_links(async_ses assert all(source.filename.endswith(".pdf") for source in sources) assert all("A_page" not in source.filename and "b_page" not in source.filename for source in sources) assert all(Path(source.filename).stem == str(source.id) for source in sources) - assert all( - source.file_path == f"documents/{document.id}/{source.filename}" - for source in sources - ) + assert all(source.file_path == f"documents/{document.id}/{source.filename}" for source in sources) assert [source.file_hash for source in sources] == [ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d", diff --git a/tests/services/test_transcription_external.py b/tests/services/test_transcription_external.py index 35553f3..efd02c3 100644 --- a/tests/services/test_transcription_external.py +++ b/tests/services/test_transcription_external.py @@ -43,13 +43,7 @@ pytestmark = [ def _real_image_paths() -> list[Path]: if not REAL_IMAGES_DIR.exists(): return [] - return sorted( - [ - p - for p in REAL_IMAGES_DIR.iterdir() - if p.is_file() and p.suffix.lower() in SUPPORTED_EXTENSIONS - ] - ) + return sorted([p for p in REAL_IMAGES_DIR.iterdir() if p.is_file() and p.suffix.lower() in SUPPORTED_EXTENSIONS]) def _artifact_filename(image_path: Path) -> str: @@ -78,11 +72,7 @@ class TestRealImageExternalTranscription: ARTIFACTS_DIR.mkdir(parents=True, exist_ok=True) artifact_path = ARTIFACTS_DIR / _artifact_filename(image_path) artifact_text = ( - f"source: {image_path.name}\n" - f"provider: {result.provider}\n" - f"model: {result.model}\n" - "---\n" - f"{result.text}\n" + f"source: {image_path.name}\nprovider: {result.provider}\nmodel: {result.model}\n---\n{result.text}\n" ) artifact_path.write_text(artifact_text, encoding="utf-8") assert artifact_path.exists() diff --git a/tests/services/test_workflows_reliability.py b/tests/services/test_workflows_reliability.py index 2fb22ca..d546e94 100644 --- a/tests/services/test_workflows_reliability.py +++ b/tests/services/test_workflows_reliability.py @@ -107,15 +107,12 @@ class TestWorkflowReliability: async with services.jobs._session_scope() as session: attempts = ( - ( - await session.exec( - select(ExecutionAttempt).where( - col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) - ) + await session.exec( + select(ExecutionAttempt).where( + col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) ) ) - .all() - ) + ).all() error_detail = next(attempt.error_detail for attempt in attempts if attempt.error_detail is not None) assert "timed out" in error_detail.lower() assert "20.0s" in error_detail @@ -179,15 +176,12 @@ class TestWorkflowReliability: async with services.jobs._session_scope() as session: attempts = ( - ( - await session.exec( - select(ExecutionAttempt).where( - col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) - ) + await session.exec( + select(ExecutionAttempt).where( + col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) ) ) - .all() - ) + ).all() assert len(attempts) == 1 duration_ms = attempts[0].duration_ms @@ -242,15 +236,12 @@ class TestWorkflowReliability: async with services.jobs._session_scope() as session: attempts = ( - ( - await session.exec( - select(ExecutionAttempt).where( - col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) - ) + await session.exec( + select(ExecutionAttempt).where( + col(ExecutionAttempt.job_source_id).in_([js.id for js in result.job_sources]) ) ) - .all() - ) + ).all() assert len(attempts) == 1 attempt = attempts[0] timing = (attempt.normalized_metadata or {}).get("processing_timing") diff --git a/tests/test_db.py b/tests/test_db.py index b314b4f..b4f810b 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -163,16 +163,13 @@ async def test_create_all_declares_hot_path_indexes(tmp_path): ) } job_source_unique = [ - constraint["column_names"] - for constraint in database.get_unique_constraints("job_source") + constraint["column_names"] for constraint in database.get_unique_constraints("job_source") ] document_tag_unique = [ - constraint["column_names"] - for constraint in database.get_unique_constraints("document_tag") + constraint["column_names"] for constraint in database.get_unique_constraints("document_tag") ] person_tag_unique = [ - constraint["column_names"] - for constraint in database.get_unique_constraints("person_tag") + constraint["column_names"] for constraint in database.get_unique_constraints("person_tag") ] return indexes, job_source_unique, document_tag_unique, person_tag_unique @@ -252,11 +249,11 @@ async def test_reconcile_legacy_job_source_columns_drops_executed_at(tmp_path): await connection.execute( text( 'create table "job_source" (' - 'id char(32) not null primary key, ' - 'job_id char(32) not null, ' - 'source_id char(32) not null, ' - 'status varchar(11) not null, ' - 'executed_at datetime not null, ' + "id char(32) not null primary key, " + "job_id char(32) not null, " + "source_id char(32) not null, " + "status varchar(11) not null, " + "executed_at datetime not null, " 'constraint "uq_job_source_job_source" unique ("job_id", "source_id"), ' 'foreign key("job_id") references "job" ("id"), ' 'foreign key("source_id") references "source" ("id")' @@ -291,14 +288,14 @@ async def test_reconcile_canonical_media_paths_normalizes_source_and_photo_paths await connection.execute( text( 'insert into "person" (id, given_names, last_name, created_at, updated_at) ' - 'values (:id, :given_names, :last_name, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + "values (:id, :given_names, :last_name, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ), {"id": "11" * 16, "given_names": "Portrait", "last_name": "Person"}, ) await connection.execute( text( 'insert into "photo" (id, person_id, path, is_primary, created_at, updated_at) ' - 'values (:id, :person_id, :path, :is_primary, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + "values (:id, :person_id, :path, :is_primary, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ), { "id": "44" * 16, @@ -310,7 +307,7 @@ async def test_reconcile_canonical_media_paths_normalizes_source_and_photo_paths await connection.execute( text( 'insert into "document" (id, name, created_at, updated_at) ' - 'values (:id, :name, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + "values (:id, :name, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ), {"id": "22" * 16, "name": "Doc"}, ) diff --git a/tests/test_meta_contract_guards.py b/tests/test_meta_contract_guards.py index 5ca6013..db7a5dd 100644 --- a/tests/test_meta_contract_guards.py +++ b/tests/test_meta_contract_guards.py @@ -109,10 +109,7 @@ def _declared_env_example_keys(*, include_commented: bool) -> set[str]: def _active_env_example_values() -> dict[str, str]: text = _read(".env.example") - return { - key: value.strip() - for key, value in re.findall(r"^\s*([A-Z0-9_]+)\s*=\s*(.*)$", text, flags=re.MULTILINE) - } + return {key: value.strip() for key, value in re.findall(r"^\s*([A-Z0-9_]+)\s*=\s*(.*)$", text, flags=re.MULTILINE)} def _normalize_env_path_value(value: str | None) -> str | None: diff --git a/tests/test_orphan_sweep.py b/tests/test_orphan_sweep.py index d551d29..7db825e 100644 --- a/tests/test_orphan_sweep.py +++ b/tests/test_orphan_sweep.py @@ -57,9 +57,7 @@ def _source_files() -> list[Path]: def _is_registered_with_framework(node: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef) -> bool: - return any( - ast.unparse(decorator).startswith(REGISTRATION_DECORATOR_PREFIXES) for decorator in node.decorator_list - ) + return any(ast.unparse(decorator).startswith(REGISTRATION_DECORATOR_PREFIXES) for decorator in node.decorator_list) def _public_definitions() -> dict[str, str]: diff --git a/tests/test_storage_reconciliation.py b/tests/test_storage_reconciliation.py index fe6b12c..d0cb254 100644 --- a/tests/test_storage_reconciliation.py +++ b/tests/test_storage_reconciliation.py @@ -35,9 +35,7 @@ async def _document_ids(settings: Settings) -> set[str]: async def _source_counts_by_document(settings: Settings) -> dict[str, int]: async with session_scope(settings=settings) as session: - rows = await session.exec( - select(Source.document_id, func.count(Source.id)).group_by(Source.document_id) - ) + rows = await session.exec(select(Source.document_id, func.count(Source.id)).group_by(Source.document_id)) return {str(document_id): int(count) for document_id, count in rows} @@ -93,9 +91,7 @@ async def assert_storage_reconciliation(*, upload_dir: Path, settings: Settings) if mismatches: report = "\n".join(f"- {item}" for item in mismatches) raise AssertionError( - "Storage reconciliation mismatch(es) detected.\n" - f"Reconciling item count: {len(mismatches)}\n" - f"{report}" + f"Storage reconciliation mismatch(es) detected.\nReconciling item count: {len(mismatches)}\n{report}" ) diff --git a/tests/test_ui_boundaries.py b/tests/test_ui_boundaries.py index 45c6772..a067d3a 100644 --- a/tests/test_ui_boundaries.py +++ b/tests/test_ui_boundaries.py @@ -129,11 +129,7 @@ def _notifies_negative(tree: ast.Module) -> bool: ): continue for keyword in node.keywords: - if ( - keyword.arg == "type" - and isinstance(keyword.value, ast.Constant) - and keyword.value.value == "negative" - ): + if keyword.arg == "type" and isinstance(keyword.value, ast.Constant) and keyword.value.value == "negative": return True return False @@ -149,6 +145,4 @@ def test_no_page_hand_rolls_error_notifications(): offenders = sorted( path.stem for path in _page_paths() if _notifies_negative(ast.parse(path.read_text(encoding="utf-8"))) ) - assert offenders == [], ( - f"Pages must render errors via error_presenter.show_error, not ui.notify: {offenders}" - ) + assert offenders == [], f"Pages must render errors via error_presenter.show_error, not ui.notify: {offenders}" diff --git a/tests/tools/test_export_import_migration.py b/tests/tools/test_export_import_migration.py index 3ca7a08..6455dbe 100644 --- a/tests/tools/test_export_import_migration.py +++ b/tests/tools/test_export_import_migration.py @@ -153,7 +153,7 @@ def test_export_import_migration_backfills_legacy_portraits_and_homepage_images( connection.execute( text( 'insert into "person" (id, full_name, portrait_path, created_at, updated_at) ' - 'values (:id, :full_name, :portrait_path, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + "values (:id, :full_name, :portrait_path, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ), {"id": person_id, "full_name": "Legacy Portrait", "portrait_path": "persons/legacy/portrait.png"}, ) @@ -264,7 +264,7 @@ def test_migration_backfills_legacy_media_when_photo_table_contains_stale_rows(t connection.execute( text( 'insert into "person" (id, full_name, portrait_path, created_at, updated_at) ' - 'values (:id, :full_name, :portrait_path, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)' + "values (:id, :full_name, :portrait_path, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ), {"id": person_id, "full_name": "Legacy Portrait", "portrait_path": "persons/legacy/portrait.png"}, ) diff --git a/tests/ui/conftest.py b/tests/ui/conftest.py index cd89e4a..ac2d4d9 100644 --- a/tests/ui/conftest.py +++ b/tests/ui/conftest.py @@ -56,8 +56,7 @@ def app_client(tmp_path_factory: pytest.TempPathFactory) -> Generator[tuple[Fast expected_path = Path(database.path).resolve() if runtime_path != expected_path: raise RuntimeError( - "Refusing to initialize destructive UI fixtures against " - f"{runtime_path}; expected {expected_path}" + f"Refusing to initialize destructive UI fixtures against {runtime_path}; expected {expected_path}" ) yield app, client diff --git a/tests/ui/test_formatters.py b/tests/ui/test_formatters.py index b553bb5..8c103b5 100644 --- a/tests/ui/test_formatters.py +++ b/tests/ui/test_formatters.py @@ -30,12 +30,8 @@ def test_person_selector_label_disambiguates_without_changing_identity(): def test_family_search_url_uses_fixed_person_details_route(): - assert family_search_url("G8T4-MDQ") == ( - "https://www.familysearch.org/tree/person/details/G8T4-MDQ" - ) + assert family_search_url("G8T4-MDQ") == ("https://www.familysearch.org/tree/person/details/G8T4-MDQ") def test_google_maps_search_url_encodes_place_query(): - assert google_maps_search_url("New York, NY") == ( - "https://www.google.com/maps/search/?api=1&query=New+York%2C+NY" - ) + assert google_maps_search_url("New York, NY") == ("https://www.google.com/maps/search/?api=1&query=New+York%2C+NY") diff --git a/tests/ui/test_jobs_page.py b/tests/ui/test_jobs_page.py index 056eaa5..4331c75 100644 --- a/tests/ui/test_jobs_page.py +++ b/tests/ui/test_jobs_page.py @@ -184,9 +184,7 @@ class TestJobsPageRendering: assert "Delete is blocked while the job is processing." in response.text @pytest.mark.asyncio - async def test_job_delete_page_allows_deletion_for_queued_job( - self, app_client, seed_document_with_unlinked_job - ): + async def test_job_delete_page_allows_deletion_for_queued_job(self, app_client, seed_document_with_unlinked_job): _, client = app_client _, job_id = seed_document_with_unlinked_job diff --git a/tests/ui/test_upload_page.py b/tests/ui/test_upload_page.py index e695ae0..33808c8 100644 --- a/tests/ui/test_upload_page.py +++ b/tests/ui/test_upload_page.py @@ -31,7 +31,7 @@ class TestPageRendering: assert response.status_code == 200 assert "Home" in response.text assert "Edit Home Page" in response.text - assert '/homepage' in response.text + assert "/homepage" in response.text def test_homepage_edit_page_renders(self, app_client): """GET /ui/homepage/edit renders the edit page.""" diff --git a/tools/migrate_v46_to_v47.py b/tools/migrate_v46_to_v47.py index ad82a07..32b9fc1 100644 --- a/tools/migrate_v46_to_v47.py +++ b/tools/migrate_v46_to_v47.py @@ -132,9 +132,7 @@ def _preflight(connection: Connection, *, strict: bool) -> None: def rotate_stored_images(connection: Connection, *, dry_run: bool) -> int: """Step 1: rewrite every mis-oriented stored image and its recorded digest.""" source = SQLModel.metadata.tables["source"] - rows = connection.execute( - select(source.c.id, source.c.file_path, source.c.filename) - ).all() + rows = connection.execute(select(source.c.id, source.c.file_path, source.c.filename)).all() rotated = 0 missing = 0 diff --git a/tools/migrate_v47_to_v48.py b/tools/migrate_v47_to_v48.py index 373754f..cc06d62 100644 --- a/tools/migrate_v47_to_v48.py +++ b/tools/migrate_v47_to_v48.py @@ -55,10 +55,7 @@ def _uniqueness_already_enforced(connection: Connection) -> bool: if any(constraint.get("name") == UNIQUE_NAME for constraint in unique_constraints): return True indexes = inspector.get_indexes("job_source") - return any( - index.get("name") == UNIQUE_NAME and index.get("unique") is True - for index in indexes - ) + return any(index.get("name") == UNIQUE_NAME and index.get("unique") is True for index in indexes) def _choose_keeper( @@ -100,11 +97,7 @@ def deduplicate_job_source_membership(connection: Connection, *, dry_run: bool) job_source.c.source_id, func.max(execution_attempt.c.created_at).label("latest_attempt_at"), ) - .select_from( - job_source.outerjoin( - execution_attempt, execution_attempt.c.job_source_id == job_source.c.id - ) - ) + .select_from(job_source.outerjoin(execution_attempt, execution_attempt.c.job_source_id == job_source.c.id)) .group_by(job_source.c.id, job_source.c.job_id, job_source.c.source_id) ) .mappings() @@ -150,13 +143,9 @@ def add_job_source_uniqueness(connection: Connection, *, dry_run: bool) -> None: dialect = connection.dialect.name if dialect == "postgresql": - statement = text( - f'alter table "job_source" add constraint "{UNIQUE_NAME}" unique ("job_id", "source_id")' - ) + statement = text(f'alter table "job_source" add constraint "{UNIQUE_NAME}" unique ("job_id", "source_id")') else: - statement = text( - f'create unique index "{UNIQUE_NAME}" on "job_source" ("job_id", "source_id")' - ) + statement = text(f'create unique index "{UNIQUE_NAME}" on "job_source" ("job_id", "source_id")') print(f" applying {UNIQUE_NAME}") if not dry_run: connection.execute(statement)