Repository navigation
Fix the critical items from the 2026-09-25 repo review - #136
Conversation
Addresses C1-C10 in docs/reviews/2026-09-25-repo-review.md: - C1: Observer's default handlers call super() instead of calling themselves forever. - C2: ProgressListener.on_begin_operation_string calls super() instead of calling itself forever. - C3: DaStreamReader::ReadBinaryBytes checks the block lengths it reads from the file before using them, sizes its decode buffer for the longest decode, and fails on truncated data. iBase64Decode rejects a length of 1 mod 4, where boost's decoder steps past the end of the source, and returns a failure for a non-base64 character instead of letting boost's dataflow_exception escape. - C4: ReadBinaryBytes rejects a block bigger than the destination. - C5: DaStreamWriter::SetBinaryBlockSize rejects a non-positive size, which made WriteBinaryBytes loop forever. - C6: stSplit without compression gives n + 1 fields for n delimiters, and an empty source gives one empty field instead of reading past the end. - C7: XM_ENSURE_FALSE_T_5 expands to XM_ENSURE_TRUE_T_5, so it compiles. - C8: Pt3d and Pt4d operator< compare z in the right direction. - C9: IntPairFromPyIter returns the pair's values instead of (1, 1). - C10: PublicProgressListener uninstalls its global listener when it is destroyed, so the listener cannot call back into a deleted object. Each fix has a regression test. The Doxygen for SetBinaryBlockSize, iBase64Decode and stSplit is updated to match.
|
Review of PR #136 — 19 findings Review: PR #136, fixes for C1–C10 from the 2026-09-25 repo review (
|
ProgressListener.on_begin_operation_string reported stack index 0, the return value of the C++ base stub, while Progress tags the progress, end and update events that follow with a 1-based depth. The Python class now tracks that depth itself, reports and returns it on begin, and steps it back on end. The Listener.cpp docs and getting_started.rst now say the outermost operation is 1. DaStreamReader::ReadVecInt, ReadVecDbl and ReadVecPt3d ignored the result of ReadBinaryBytes, so a corrupt binary block loaded as a zeroed or partly filled vector and the read reported success. They now return false. ReadBinaryBytes also requires the BINARY_BLOCK keyword and logs an error for a bad header and for truncated data, so every rejection leaves a message. Observer's default handlers are now empty instead of calling super(), which matches the empty C++ base handlers without relying on pybind11's frame check to avoid recursion. PublicProgressListener::impl deletes its copy operations, since its destructor uninstalls the global listener. Tests: every bad-block row checks its logged error, a good block reads with and without a trailing newline, the three vector readers reject a bad block, and the progress listener reports 1 and 2 for nested operations.
|
Pushed b3c4db4, which responds to the review above. I've updated the PR description to match it. Fixed
Not changed
Local runs:
|
The public ProgressListener docs said the stack index is 0 for the first operation, but Listener and the test mock both return 1 for the outermost, as getting_started.rst already says. The docs now say 1 and note that an operation begun before a listener was installed reports 0. The OnUpdateMessage briefs no longer describe the end event. PublicProgressListener and Listener delete a raw impl pointer in their destructors, so a copy of either would delete it twice. Both now disable copying. The Python ProgressListener documents that overrides of the begin and end handlers must call super(), and a new test pins that an end with no matching begin leaves the next operation at stack index 1. The binary vector test checks the log after each reader instead of once at the end.
|
Pushed b962563, a follow-up to b3c4db4. A second self-review of that round found gaps:
Local runs:
|
Fixes the ten CRITICAL items (C1–C10) in
docs/reviews/2026-09-25-repo-review.md. C1–C8 each come with a regression test. C9 and C10 live in the pybind-only sources, which no test target builds, so they were checked by hand from Python (see follow-ups). The Doxygen for the changed functions is updated in the same commits.Observer's default handlers no longer call themselves forever. They are empty, like the C++ base handlers.observer_pyt.py::TestObserverDefaultHandlersProgressListener.on_begin_operation_stringno longer calls itself. It reports and returns the operation's 1-based stack index, the same oneProgresspasses to the later events.progress_listener_pyt.pyDaStreamReader::ReadBinaryByteschecks the lengths it reads from the file before using them, sizes its decode buffer for the longest decode, and fails on truncated data.iBase64Decoderejects input it can't decode safely (see below).testReadBinaryBytesRejectsBadBlocks,testReadBinaryBytesReadsGoodBlockReadBinaryBytesrejects a block bigger than the destinationtestReadBinaryBytesRejectsBadBlocksSetBinaryBlockSizerejects a non-positive size, which madeWriteBinaryBytesloop forevertestSetBinaryBlockSizeRejectsNonPositivestSplitwithout compression gives n + 1 fields for n delimiters; an empty source gives one empty fieldtestSplitXM_ENSURE_FALSE_T_5expands toXM_ENSURE_TRUE_T_5, so it compilesXmErrorUnitTests::test1Pt4's twooperator<overloads compare z in the right directionPtUnitTests::testItIntPairFromPyIterreturns the pair's values instead of(1, 1)PublicProgressListeneruninstalls its global listener when destroyed, so the listener can't call back into a deleted objectNotes for reviewers
iBase64Decodesat under the C3 fix.transform_widthcompares only its underlying positions, and reads ahead before each output byte. When the input (with=padding stripped) is 1 mod 4 characters long, the decode loop never matchesendand reads past the buffer.dataflow_exceptionout of a function that returnsbool.ReadBinaryBytesreturn false. Each bad input in the test must log the error from the check meant to reject it, so deleting a check fails the test.ReadBinaryBytesreturns false on a malformed block instead of overrunning its buffers or throwing. It logs an error for every rejection, and the header must start withBINARY_BLOCK.ReadVecInt,ReadVecDblandReadVecPt3dreturn false when their binary block is bad. They used to return true with a zeroed or partly filled vector.ProgressListener'sbegin_operationcallback reports the operation's stack index (1 for the outermost) instead of 0.PublicProgressListenerandListenercan no longer be copied. A copy would have deleted theirimpltwice.SetBinaryBlockSize(<= 0)asserts in debug and keeps the current size.stSplit(..., false)now returns the last field. It used to drop it, e.g."A,B"gave{"A"}.Testing
Run locally on Windows with MSVC 194 (the
testing-dynamic-debugandpython-py314-dynamic-releasepresets):Running cxxtest tests (73 tests)...OK!Ran 30 tests ... OKxmsconan job lint:flake8 _packagereports 0pt.cppassertions.RecursionError.testReadBinaryBytesRejectsBadBlocks.ReadVecIntignore theReadBinaryBytesresult again, or dropping theBINARY_BLOCKcheck, fails the new tests.test_unmatched_end_operation.Follow-ups (not in this PR)
IntPairFromPyIterandPublicProgressListenerare built only into the Python module, so neither CxxTest nor the Python suite can see the installed listener. Covering them needs a CxxTest target that builds the pybind sources, or a test-only binding forProgressListener::GetListener.GetListener(), or a listener destroyed from inside its own callback, can still be left dangling. Fixing that needs a way to detach the listener (overlaps M23).update_messagealways reports stack index -1.testSetBinaryBlockSizeRejectsNonPositivesaves and restoresxmAsserting()by hand. SharingXmAssertScopedDisablewould mean adding it to the shippedTestTools.h.iUncompresshas an empty\briefand a closing comment that saysiCompress.