Skip to content

PTV-1904 refactor summary file merging to fix bug - #101

Merged
briehl merged 8 commits into
PTV-1904-add-ghafrom
PTV-1904-tree-map-cleanup
Dec 2, 2025
Merged

PTV-1904 refactor summary file merging to fix bug#101
briehl merged 8 commits into
PTV-1904-add-ghafrom
PTV-1904-tree-map-cleanup

Conversation

@briehl

@briehl briehl commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Ok, so the real actual bug in PTV-1904 comes from the summary files. The "fix" put in #92 fixed it kinda by accident. The real issue was in how the summary TSV files were loaded, namely, that they were loaded by stripping all whitespace off the end of the lines and then splitting on \t. There are uncommon cases where the last entries in some lines are just empty - some files put in N/A, others just leave those elements blank. Using rstrip on those lines just kinda caused some chaos.

This is a reasonably mild refactor of the piece of _process_output_files that addresses this. There's a separate function for loading summary files, and for merging them. The merge process is still mostly the same, but loading the files should be more stable now.

@briehl briehl mentioned this pull request Dec 1, 2025
@briehl

briehl commented Dec 1, 2025

Copy link
Copy Markdown
Contributor Author

Claude review:

PR Review: PTV-1904 refactor summary file merging to fix bug

Overview

This PR addresses the root cause of the PTV-1904 bug by refactoring the summary TSV file loading and merging logic. The issue stems from the use of rstrip() which removes trailing whitespace, causing problems with files that have empty trailing fields. The solution extracts specialized functions for loading and merging summary files with proper field handling.

Changes Summary

Files Modified:

  • lib/kb_gtdbtk/core/gtdbtk_runner.py - Core refactoring: new helper functions, improved documentation
  • test/core/gtdbtk_runner_test.py - Comprehensive new tests for refactored functions

Statistics:

  • Additions: 474 lines
  • Deletions: 60 lines
  • Net change: ~414 lines of new/improved code

Detailed Review

Positive Aspects:

  1. Root Cause Fix - Identifies and fixes the actual bug:

    • Old approach: line.rstrip().split("\t") removed trailing whitespace, causing empty fields to disappear
    • New approach: line.rstrip("\n\r").split("\t") only removes line endings, preserving trailing tabs and empty fields
    • This is the real fix that prevents data corruption
  2. Code Refactoring - Extracts two well-defined helper functions:

    • _load_summary_tsv_file(filepath) - Handles TSV loading with proper field preservation
    • _merge_summary_tsv_files(std_path, tree_path, output_path) - Handles merging logic
    • Makes code more testable and maintainable
    • Reduces complexity in the main _process_output_files() function
  3. Excellent Documentation:

    • _process_output_files() now has a comprehensive 50+ line docstring explaining:
      • Purpose and overall flow
      • All parameters with detailed descriptions
      • Return value structure and semantics
      • Subdirectory structure and file organization
    • New helper functions have clear, focused docstrings
    • Function comments clarify the logic
  4. Comprehensive Test Coverage - New test suite with 13 test cases:

    • TestLoadSummaryTsvFile: Tests for the file loader (3 tests)
      • Basic loading, N/A handling, trailing field preservation
    • TestMergeSummaryTsvFiles: Tests for the merge logic (6 tests)
      • Both files, only std, only tree, no files, mismatched headers
      • Crucially: Tests preserve trailing empty fields
    • TestProcessOutputFiles: Integration tests (4 tests)
      • JSON creation, classification mapping, null value handling
      • ID mapping file creation and error handling
  5. Proper Error Handling:

    • _merge_summary_tsv_files() handles 4 file existence scenarios gracefully
    • Logs warning when headers don't match and falls back to tree file
    • _process_output_files() raises ValueError with descriptive message for missing IDs
  6. Edge Case Testing - Tests cover:

    • Trailing empty fields (the original bug)
    • Whitespace preservation
    • Empty N/A fields
    • Mismatched headers
    • Missing files
    • ID remapping errors
  7. Type Hints - Good use of type hints in new functions:

    • _load_summary_tsv_file(filepath: Path) -> dict[str, list|dict]
    • _merge_summary_tsv_files(std_path: Path, tree_path: Path, output_path: Path)
  8. Improved Code Comments - Better inline documentation:

    • Explains temp_output vs temp_trees_output distinction
    • Clarifies preference order for tree files
    • Documents why unit test checks for runtime_output directory

Observations & Questions

  1. Type Hint Syntax - Line 357 uses dict[str, list|dict] (Python 3.10+ syntax):

    • Is this consistent with the project's minimum Python version?
    • Consider using Dict[str, Union[List, Dict]] for broader compatibility if needed
  2. rstrip("\n\r") vs rstrip() - The fix only removes \n\r:

    • What about files with other line endings (e.g., old Mac \r only)?
    • Current approach should handle all standard line endings
    • Works correctly on Unix, Windows, and old Mac systems
  3. Test Data Files - The tests use tmp_path fixtures and synthetic data:

  4. Empty Field Representation - In _process_output_files():

    • Empty fields are replaced with "-" (line ~365 in full code)
    • Is this consistent with GTDB-tk's output format?
    • Should this be documented or configurable?
  5. Performance - The refactored code:

    • Loads entire files into memory (acceptable for typical file sizes)
    • Creates dictionaries for fast lookups (good design)
    • No performance concerns for typical TSV files

Potential Concerns

  1. Subtle Change in Behavior - The old code used rstrip() which removed all trailing whitespace:

    • Old: "value1\t\t\n" → split → ["value1"]
    • New: "value1\t\t\n" → split → ["value1", "", ""]
    • This is intentional and fixes the bug, but verify no downstream code relied on the old behavior
  2. Header Mismatch Logging - Line 347 logs a warning but silently uses tree_path:

    • Should this raise an error instead of silently falling back?
    • Current approach is defensive but might hide issues
    • Consider if this should be configurable
  3. Comment Needs Update - Line 237 says "should only occur during unit tests":

    • Verify that the runtime_output directory is never created in production
    • If it can occur in production, clarify when and why

Test Quality Assessment

Strengths:

  • Tests are well-organized into logical test classes
  • Each test is focused and tests one thing
  • Good use of parametric test cases
  • Clear test names that describe what's being tested

Coverage:

  • Happy paths: ✅
  • Edge cases: ✅
  • Error conditions: ✅
  • Integration between functions: ✅

Verdict

APPROVED - This is excellent refactoring that fixes a critical bug while improving code quality and maintainability. The root cause is properly identified and fixed, the refactoring is clean, and the test coverage is comprehensive.

Key strengths:

  • Fixes the actual root cause (trailing whitespace handling)
  • Well-documented code
  • Excellent test coverage (13 new tests)
  • Cleaner, more maintainable code structure

Minor recommendations:

  1. Verify that the change from rstrip() to rstrip("\n\r") doesn't break any downstream consumers
  2. Consider whether header mismatch should raise an error or log+continue (current approach is reasonable but could be more defensive)
  3. Add integration test using the problematic genome data from PR PTV-1904 fix local tests #92
  4. Document the - placeholder for empty fields if it's user-visible

The PR is ready to merge with confidence that the PTV-1904 bug is properly fixed.

PTV-1904 update release notes, bump version to 1.4.1
@briehl
briehl merged commit ff55d07 into PTV-1904-add-gha Dec 2, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant