Skip to content

Latent bugs found while adopting a type checker (MemoryStorage backend parity, gunicorn-less server start, and others) #102

Description

@r0ny123

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 absentmcrit/__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": ...}) raisesMemoryStorage.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].

    st.modifyFamily(1, {"is_library": True})  ->  KeyError: 'sample_id'
    
  • 3. MemoryStorage.deleteFamily(..., keep_samples=True) raisesMemoryStorage.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 faultsMemoryStorage.py:820, :839, :860
    Each one masks the next, so they only surface one at a time:

    1. a FunctionEntry is subscripted as a dict (entry["function_id"]);
    2. for function_id, entry in self._functions: iterates keys rather than .items();
    3. 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 matchMemoryStorage.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.

    st.getMatchesForPicBlockHash(0x816da97373fe8cb8)  ->  TypeError: unhashable type: 'list'
    
  • 6. MemoryStorage query-sample accessors do not accept is_queryMemoryStorage.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.

    st.getSamples(start_index=0, limit=0, is_query=True)
    -> TypeError: MemoryStorage.getSamples() got an unexpected keyword argument 'is_query'
    
  • 7. LocalQueue._file_to_grid raises on str inputLocalQueue.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 entryMemoryStorage.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 sampleMongoDbStorage.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 existMcritConsole.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.

Not fixed in #101

  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions