Problem
NotebookPath.__eq__() compares resolved paths, but __hash__() hashes unresolved internal state. Equal paths can therefore have different hashes, breaking dict/set lookup.
There is also a nearby relative_to() edge case: making one relative path relative to an identical relative path crashes because it calls NotebookPath(*empty_parts) with no required first argument.
Minimum repro: equal paths with different hashes
from labapi import NotebookPath
anchored = NotebookPath("b", parent=NotebookPath("/a"))
absolute = NotebookPath("/a/b")
print(anchored == absolute)
print(hash(anchored) == hash(absolute))
print({anchored: "value"}.get(absolute, "missing"))
Actual:
Expected: if two paths compare equal, their hashes must also compare equal.
Minimum repro: relative_to identical relative path
from labapi import NotebookPath
NotebookPath("a").relative_to(NotebookPath("a"))
Actual: TypeError: NotebookPath.__init__() missing 1 required positional argument: 'part'.
Expected: return an empty relative path or a documented PathError, not an internal constructor error.
Pointers
src/labapi/util/path.py, NotebookPath.__eq__() resolves paths before comparison.
src/labapi/util/path.py, NotebookPath.__hash__() hashes unresolved state.
src/labapi/util/path.py, relative_to() calls NotebookPath(*self.escape(*self[len(other):])) for relative prefixes.
Problem
NotebookPath.__eq__()compares resolved paths, but__hash__()hashes unresolved internal state. Equal paths can therefore have different hashes, breaking dict/set lookup.There is also a nearby
relative_to()edge case: making one relative path relative to an identical relative path crashes because it callsNotebookPath(*empty_parts)with no required first argument.Minimum repro: equal paths with different hashes
Actual:
Expected: if two paths compare equal, their hashes must also compare equal.
Minimum repro: relative_to identical relative path
Actual:
TypeError: NotebookPath.__init__() missing 1 required positional argument: 'part'.Expected: return an empty relative path or a documented
PathError, not an internal constructor error.Pointers
src/labapi/util/path.py,NotebookPath.__eq__()resolves paths before comparison.src/labapi/util/path.py,NotebookPath.__hash__()hashes unresolved state.src/labapi/util/path.py,relative_to()callsNotebookPath(*self.escape(*self[len(other):]))for relative prefixes.