You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While adopting the ty type checker in #101 I ran into a set of latent bugs. Opening this to track them independently of that PR, since they are worth reviewing on their own merits and a few are arguably worth fixing sooner than the tooling change lands.
All of these are present on main @ 9e45560. Everything in the first section was reproduced by running the code against that commit; the exact exception is quoted. The MongoDbStorage items are code-level (they need a live database to trigger) and are quoted with their line.
Items 1–11 are already fixed in #101. Item 12 is not fixed there.
Note on item 4: when I first wrote this I had only found two of the three faults in getUniqueBlocks. The third became visible once the first two were fixed. It is described in full below and is also fixed in #101, along with tests for getUniqueBlocks and getMatchesForPicBlockHash — neither path was reachable on the memory backend before, so the shared test suite never exercised them.
Reproduced on main
1. mcrit server cannot start where gunicorn is absent — mcrit/__main__.py
The import sits in try/except, but class gunicornServer(BaseApplication): executes unconditionally, so a failed import leaves the name unbound. gunicorn is excluded on Windows by dependency marker (gunicorn; platform_system != "Windows"), so the server is unstartable there — the failure happens before the platform == "linux" check that was meant to guard it.
runServer() with gunicorn unimportable
-> UnboundLocalError: cannot access local variable 'BaseApplication' where it is not associated with a value
2. MemoryStorage.modifyFamily(..., {"is_library": ...}) raises — MemoryStorage.py:276 self._samples["sample_id"] uses the literal string as a key into a dict keyed by int. Looks like a typo for self._samples[sample_id].
3. MemoryStorage.deleteFamily(..., keep_samples=True) raises — MemoryStorage.py:321-322,328
Uses item assignment (entry["family_id"] = 0) on SampleEntry / FunctionEntry, which are plain objects.
st.deleteFamily(1, keep_samples=True) -> TypeError: 'SampleEntry' object does not support item assignment
4. MemoryStorage.getUniqueBlocks raises — three separate faults — MemoryStorage.py:820, :839, :860
Each one masks the next, so they only surface one at a time:
a FunctionEntry is subscripted as a dict (entry["function_id"]);
for function_id, entry in self._functions: iterates keys rather than .items();
the block-instruction lookup uses entry.xcfg["blocks"][str(block_offset)]. That is correct for MongoDbStorage, where the xcfg is stored as JSON and its block keys are consequently strings — but in MemoryStorage the xcfg is the live dict from SmdaFunction.toDict(), whose block keys are int, so this raises for every block.
st.getUniqueBlocks([0]) -> TypeError: 'FunctionEntry' object is not subscriptable
... and after fixing 1 and 2: KeyError: '0'
5. MemoryStorage.getMatchesForPicBlockHash raises on any real match — MemoryStorage.py:551 result.add([...]) adds a list to a set. Only reached when the hash actually matches a block, which is why it survives a lookup that finds nothing.
6. MemoryStorage query-sample accessors do not accept is_query — MemoryStorage.py:625,649 MongoDbStorage.getSamples / getSampleBySha256 take is_query, the memory versions do not — so Worker.py:196, the query-sample cleanup path, breaks on the memory backend.
7. LocalQueue._file_to_grid raises on str input — LocalQueue.py:418 data = data.encode(self.encoding) reads data before it is assigned, and LocalQueue has no encoding attribute. The surrounding except AttributeError does not catch it.
q._file_to_grid("hello") -> UnboundLocalError: cannot access local variable 'data' ...
Code-level (need a live database, or produce silently wrong data)
8. MemoryStorage.addStorageContent writes a malformed pichash entry — MemoryStorage.py:771
Stores (sample_id, function_id) where every other site — including the declaration at MemoryStorage.py:128 — stores (family_id, sample_id, function_id). Silently corrupts the pichash index on import rather than raising.
9. MongoDbStorage.getCandidatesForMinHash returns None, not set() — MongoDbStorage.py:929-931
A bare return on the empty-minhash path, in a function annotated -> Set[int]. MemoryStorage returns set() for the same input, so the two backends disagree and callers that iterate the result break on Mongo only.
10. MongoDbStorage.deleteSample returns None after deleting a query sample — MongoDbStorage.py:433
A bare return in the sample_id < 0 branch of a -> bool function, so a successful deletion reads as failure to callers.
11. mcrit client sync dispatches to a method that does not exist — McritConsole.py:310 self._handle_sync(ARGS) has no definition. It is unreachable in practice — the registered subcommands are status submit query import export search queue, and sync is not among them — so this is dead code rather than a live crash.
12. Invalid escape sequences in five modules mcrit/server/{Blocks,Status,Function}Resource.py use "^\d+..." and {Family,Sample}Resource.py use [a-zA-Z0-9._\-] in ordinary (non-raw) string literals. These emit SyntaxWarning: invalid escape sequence on import today and are slated to become a SyntaxError in a future Python. The fix is raw strings (r"^\d+..."). Fixed in Use raw strings for regex literals and enable ruff W605 #103, which also adds W605 to ruff's select list so it cannot recur; the string literals are byte-identical before and after, so there is no behaviour change.
I left these alone in Modernise packaging and CI, adopt ty, and fix the bugs it surfaced #101 because ruff's W605 is not in the selected rule set, and enabling it felt out of scope for that PR — happy to either add the rule or fix the five literals, whichever you prefer.
Reproductions were run against main @ 9e45560 with the in-repo example report, using MemoryStorage via StorageFactory. Happy to split any of these into their own issue, or to send items 1–11 as a focused bugfix PR separate from the tooling work in #101 if that is easier to review.
While adopting the
tytype checker in #101 I ran into a set of latent bugs. Opening this to track them independently of that PR, since they are worth reviewing on their own merits and a few are arguably worth fixing sooner than the tooling change lands.All of these are present on
main@ 9e45560. Everything in the first section was reproduced by running the code against that commit; the exact exception is quoted. TheMongoDbStorageitems are code-level (they need a live database to trigger) and are quoted with their line.Items 1–11 are already fixed in #101. Item 12 is not fixed there.
Reproduced on
main1.
mcrit servercannot start where gunicorn is absent —mcrit/__main__.pyThe import sits in
try/except, butclass gunicornServer(BaseApplication):executes unconditionally, so a failed import leaves the name unbound. gunicorn is excluded on Windows by dependency marker (gunicorn; platform_system != "Windows"), so the server is unstartable there — the failure happens before theplatform == "linux"check that was meant to guard it.2.
MemoryStorage.modifyFamily(..., {"is_library": ...})raises —MemoryStorage.py:276self._samples["sample_id"]uses the literal string as a key into a dict keyed byint. Looks like a typo forself._samples[sample_id].3.
MemoryStorage.deleteFamily(..., keep_samples=True)raises —MemoryStorage.py:321-322,328Uses item assignment (
entry["family_id"] = 0) onSampleEntry/FunctionEntry, which are plain objects.4.
MemoryStorage.getUniqueBlocksraises — three separate faults —MemoryStorage.py:820,:839,:860Each one masks the next, so they only surface one at a time:
FunctionEntryis subscripted as a dict (entry["function_id"]);for function_id, entry in self._functions:iterates keys rather than.items();entry.xcfg["blocks"][str(block_offset)]. That is correct forMongoDbStorage, where the xcfg is stored as JSON and its block keys are consequently strings — but inMemoryStoragethe xcfg is the live dict fromSmdaFunction.toDict(), whose block keys areint, so this raises for every block.5.
MemoryStorage.getMatchesForPicBlockHashraises on any real match —MemoryStorage.py:551result.add([...])adds a list to a set. Only reached when the hash actually matches a block, which is why it survives a lookup that finds nothing.6.
MemoryStoragequery-sample accessors do not acceptis_query—MemoryStorage.py:625,649MongoDbStorage.getSamples/getSampleBySha256takeis_query, the memory versions do not — soWorker.py:196, the query-sample cleanup path, breaks on the memory backend.7.
LocalQueue._file_to_gridraises onstrinput —LocalQueue.py:418data = data.encode(self.encoding)readsdatabefore it is assigned, andLocalQueuehas noencodingattribute. The surroundingexcept AttributeErrordoes not catch it.Code-level (need a live database, or produce silently wrong data)
8.
MemoryStorage.addStorageContentwrites a malformed pichash entry —MemoryStorage.py:771Stores
(sample_id, function_id)where every other site — including the declaration atMemoryStorage.py:128— stores(family_id, sample_id, function_id). Silently corrupts the pichash index on import rather than raising.9.
MongoDbStorage.getCandidatesForMinHashreturnsNone, notset()—MongoDbStorage.py:929-931A bare
returnon the empty-minhash path, in a function annotated-> Set[int].MemoryStoragereturnsset()for the same input, so the two backends disagree and callers that iterate the result break on Mongo only.10.
MongoDbStorage.deleteSamplereturnsNoneafter deleting a query sample —MongoDbStorage.py:433A bare
returnin thesample_id < 0branch of a-> boolfunction, so a successful deletion reads as failure to callers.11.
mcrit client syncdispatches to a method that does not exist —McritConsole.py:310self._handle_sync(ARGS)has no definition. It is unreachable in practice — the registered subcommands arestatus submit query import export search queue, andsyncis not among them — so this is dead code rather than a live crash.Not fixed in #101
mcrit/server/{Blocks,Status,Function}Resource.pyuse"^\d+..."and{Family,Sample}Resource.pyuse[a-zA-Z0-9._\-]in ordinary (non-raw) string literals. These emitSyntaxWarning: invalid escape sequenceon import today and are slated to become aSyntaxErrorin a future Python. The fix is raw strings (r"^\d+..."). Fixed in Use raw strings for regex literals and enable ruff W605 #103, which also addsW605to ruff'sselectlist so it cannot recur; the string literals are byte-identical before and after, so there is no behaviour change.I left these alone in Modernise packaging and CI, adopt ty, and fix the bugs it surfaced #101 because ruff's
W605is not in the selected rule set, and enabling it felt out of scope for that PR — happy to either add the rule or fix the five literals, whichever you prefer.Reproductions were run against
main@ 9e45560 with the in-repo example report, usingMemoryStorageviaStorageFactory. Happy to split any of these into their own issue, or to send items 1–11 as a focused bugfix PR separate from the tooling work in #101 if that is easier to review.