Upgrade Pyright from basic to strict mode (closes #291)#316
Upgrade Pyright from basic to strict mode (closes #291)#316nitrobass24 merged 16 commits intodevelopfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughNarrowed and added Python type annotations across many modules, introduced postponed-evaluation imports in select files, standardized equality/repr signatures to accept Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/controller/extract/dispatch.py`:
- Around line 62-63: The __eq__ method on ExtractStatus blindly accesses
other.__dict__, which raises AttributeError for primitives or objects without
__dict__; add a type guard similar to ModelDiff.__eq__: check isinstance(other,
ExtractStatus) (or return NotImplemented when other is not an ExtractStatus) and
only then compare self.__dict__ == other.__dict__; this preserves correct
equality semantics and lets Python fallback to other comparisons when
appropriate.
In `@src/python/controller/validate/validate_process.py`:
- Around line 61-62: The __eq__ implementation on ValidateStatus should first
check that other is an instance of ValidateStatus and return NotImplemented for
non-matching types to follow Python's equality protocol; replace the direct
other.__dict__ access in ValidateStatus.__eq__ with an isinstance(other,
ValidateStatus) guard and only then compare the relevant attributes (e.g.,
self.__dict__ == other.__dict__) so comparing to ints/strings returns
NotImplemented instead of raising AttributeError.
In `@src/python/lftp/job_status.py`:
- Around line 85-86: The __eq__ implementation in LftpJobStatus should guard
against non-LftpJobStatus inputs to avoid AttributeError; update
LftpJobStatus.__eq__ to first check isinstance(other, LftpJobStatus) and if not,
return NotImplemented, otherwise compare the two objects (e.g., via
self.__dict__ == other.__dict__); apply the same pattern to other vulnerable
__eq__ implementations you saw (in file.py, validate_process.py, dispatch.py,
diff.py) to follow PEP 207.
In `@src/python/model/diff.py`:
- Around line 25-26: The ModelDiff.__eq__ method currently accesses
other.__dict__ directly which raises AttributeError for non-ModelDiff values;
update ModelDiff.__eq__ to first check isinstance(other, ModelDiff) (mirroring
the pattern used in AutoQueuePattern.__eq__) and return NotImplemented when the
other object is not a ModelDiff, otherwise compare the dicts as before.
In `@src/python/system/file.py`:
- Around line 29-30: The __eq__ implementation on SystemFile should guard
against non-SystemFile comparisons to avoid AttributeError; modify
SystemFile.__eq__ to first check the type (e.g., isinstance(other, SystemFile))
and if it is not the same type return NotImplemented (or False), otherwise
compare the internal state (self.__dict__) as before; reference the
SystemFile.__eq__ method and ensure the function returns NotImplemented for
unsupported types so Python can handle symmetric comparisons.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 10a0b4e6-6f93-4bba-93f1-d31a6670eb8f
📒 Files selected for processing (32)
src/python/common/app_process.pysrc/python/common/config.pysrc/python/common/context.pysrc/python/common/multiprocessing_logger.pysrc/python/common/path_pairs_config.pysrc/python/common/types.pysrc/python/controller/auto_queue.pysrc/python/controller/controller.pysrc/python/controller/controller_persist.pysrc/python/controller/extract/dispatch.pysrc/python/controller/extract/extract.pysrc/python/controller/extract/extract_process.pysrc/python/controller/model_builder.pysrc/python/controller/notifier.pysrc/python/controller/validate/validate_process.pysrc/python/lftp/job_status.pysrc/python/lftp/lftp.pysrc/python/model/diff.pysrc/python/model/file.pysrc/python/pyrightconfig.jsonsrc/python/scan_fs.pysrc/python/seedsync.pysrc/python/ssh/sshcp.pysrc/python/system/file.pysrc/python/system/scanner.pysrc/python/web/handler/logs.pysrc/python/web/handler/path_pairs.pysrc/python/web/handler/stream_log.pysrc/python/web/security.pysrc/python/web/serialize/serialize_model.pysrc/python/web/web_app.pysrc/python/web/web_app_job.py
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/python/web/serialize/serialize_log_record.py (1)
31-35:⚠️ Potential issue | 🟠 MajorUse
getMessage()to guarantee string safety.
At line 35,record.msgcan be an arbitrary object (not guaranteed to be a string), which violates the declareddict[str, str | None]type and may fail during JSON serialization. The standard library providesLogRecord.getMessage()specifically to obtain the formatted message as a string.Proposed fix
- json_dict[SerializeLogRecord.__KEY_MESSAGE] = record.msg + json_dict[SerializeLogRecord.__KEY_MESSAGE] = record.getMessage()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/python/web/serialize/serialize_log_record.py` around lines 31 - 35, The JSON dict builder in SerializeLogRecord uses record.msg which can be non-string; replace that usage with record.getMessage() so the value stored under SerializeLogRecord.__KEY_MESSAGE is guaranteed to be a string and matches the declared dict[str, str | None] type. Update the assignment that sets json_dict[SerializeLogRecord.__KEY_MESSAGE] to call record.getMessage() instead of accessing record.msg, leaving other keys unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/common/path_pairs_config.py`:
- Around line 73-76: The PathPair.__eq__ currently returns False for
non-PathPair operands; change its type-check branch to return NotImplemented
(instead of False) to follow PEP 207 and match AutoQueuePattern.__eq__; locate
the __eq__ method on the PathPair class and replace the "if not
isinstance(other, PathPair): return False" behavior with "return NotImplemented"
so Python can try the reflected comparison on the other operand.
In `@src/python/ssh/sshcp.py`:
- Line 32: Remove the unreachable None-check for the parameter named host:
delete the "if host is None: # type: ignore[reportUnnecessaryComparison]"
conditional and any associated unreachable branch in sshcp.py so the function
treats host as a non-nullable str; remove the type-ignore comment and do not
replace it with another guard (call sites already provide valid strings).
---
Outside diff comments:
In `@src/python/web/serialize/serialize_log_record.py`:
- Around line 31-35: The JSON dict builder in SerializeLogRecord uses record.msg
which can be non-string; replace that usage with record.getMessage() so the
value stored under SerializeLogRecord.__KEY_MESSAGE is guaranteed to be a string
and matches the declared dict[str, str | None] type. Update the assignment that
sets json_dict[SerializeLogRecord.__KEY_MESSAGE] to call record.getMessage()
instead of accessing record.msg, leaving other keys unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bd4fc7ff-9f5d-47da-903e-c93b14099153
📒 Files selected for processing (56)
src/python/common/__init__.pysrc/python/common/config.pysrc/python/common/context.pysrc/python/common/multiprocessing_logger.pysrc/python/common/path_pairs_config.pysrc/python/common/status.pysrc/python/controller/__init__.pysrc/python/controller/auto_queue.pysrc/python/controller/controller.pysrc/python/controller/controller_persist.pysrc/python/controller/delete/__init__.pysrc/python/controller/extract/__init__.pysrc/python/controller/extract/dispatch.pysrc/python/controller/extract/extract_process.pysrc/python/controller/model_builder.pysrc/python/controller/move/__init__.pysrc/python/controller/notifier.pysrc/python/controller/scan/__init__.pysrc/python/controller/scan/active_scanner.pysrc/python/controller/scan/scanner_process.pysrc/python/controller/validate/__init__.pysrc/python/controller/validate/validate_process.pysrc/python/lftp/__init__.pysrc/python/lftp/job_status.pysrc/python/lftp/job_status_parser.pysrc/python/lftp/lftp.pysrc/python/model/__init__.pysrc/python/model/diff.pysrc/python/model/file.pysrc/python/model/model.pysrc/python/pyrightconfig.jsonsrc/python/scan_fs.pysrc/python/seedsync.pysrc/python/ssh/__init__.pysrc/python/ssh/sshcp.pysrc/python/system/__init__.pysrc/python/system/file.pysrc/python/system/scanner.pysrc/python/typings/bottle/__init__.pyisrc/python/typings/pexpect/__init__.pyisrc/python/typings/pexpect/exceptions.pyisrc/python/typings/tblib/__init__.pyisrc/python/typings/tblib/pickling_support.pyisrc/python/web/__init__.pysrc/python/web/handler/logs.pysrc/python/web/handler/path_pairs.pysrc/python/web/handler/stream_log.pysrc/python/web/security.pysrc/python/web/serialize/__init__.pysrc/python/web/serialize/serialize_auto_queue.pysrc/python/web/serialize/serialize_config.pysrc/python/web/serialize/serialize_log_record.pysrc/python/web/serialize/serialize_model.pysrc/python/web/serialize/serialize_status.pysrc/python/web/utils.pysrc/python/web/web_app.py
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/scan_fs.py`:
- Line 41: Replace all PEP 526 inline variable annotations like
"self.__children: List[SystemFile] = []" with Python 3.5 compatible type
comments by moving the annotation into a trailing "# type: ..." comment (e.g.,
keep the initializer and add "# type: List[SystemFile]" after it). Apply this
change for self.__children and any other occurrences of the ": Type = value"
pattern in this file (the ones flagged around the later variable declarations),
ensuring you preserve the existing initializers and use the same type names
(List, SystemFile, etc.) in the "# type:" comments.
In `@src/python/system/scanner.py`:
- Around line 48-50: Replace PEP 604 and builtin generic annotations with typing
equivalents for consistency: change the attributes on SystemScanner —
exclude_prefixes: list[str], exclude_suffixes: list[str],
__lftp_temp_file_suffix: str | None — to use typing.List and typing.Optional
(e.g., exclude_prefixes: List[str], exclude_suffixes: List[str],
__lftp_temp_file_suffix: Optional[str]); update any other occurrences in this
module (the other annotations using list[...] or str | None) similarly (e.g.,
method args/returns referenced at the same spots) and add the import line from
typing import List, Optional at the top.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3d4e7b98-7bea-4727-bc03-5a481549ffa4
📒 Files selected for processing (5)
src/python/common/path_pairs_config.pysrc/python/scan_fs.pysrc/python/ssh/sshcp.pysrc/python/system/scanner.pysrc/python/web/serialize/serialize_log_record.py
Switch pyrightconfig.json to strict mode with 0 errors. Changes: - Add missing parameter type annotations across 30+ files - Add type arguments to generic classes (Queue, list, dict, Callable, etc.) - Convert namedtuple to NamedTuple with typed fields (job_status.py) - Add `from __future__ import annotations` where runtime subscript of multiprocessing.Queue would fail - Add type: ignore comments for intentional patterns: - reportPrivateUsage: cross-class access in controller - reportUnusedFunction: Bottle plugin decorators in security.py - reportUnnecessaryComparison: defensive None checks - Disable reportUnknown* family (cascading noise from untyped deps: bottle, pexpect, tblib) and reportMissingTypeStubs — to be re-enabled incrementally as type stubs are added - Disable reportUnusedImport (conflicts with __init__.py re-exports) Closes #291 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Write custom type stubs for bottle, pexpect, and tblib (no upstream stubs exist). Add generic type parameters to all class attributes, method signatures, and local variables across 51 files. Use X-as-X re-export pattern in __init__.py files. Remove all reportUnknown* suppressions from pyrightconfig.json — strict mode now passes clean with zero errors and zero suppressions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Return NotImplemented for non-matching types in ExtractStatus, ValidateStatus, LftpJobStatus, ModelDiff, and SystemFile __eq__ methods, matching the existing pattern in ModelFile and AutoQueuePattern. Prevents AttributeError when comparing against primitives or unrelated objects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PathPair.__eq__: return NotImplemented instead of False for non-PathPair types (PEP 207) - Sshcp.__init__: remove unreachable None check on non-nullable host - SerializeLogRecord: use record.getMessage() instead of record.msg to ensure the message is always a string Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use getattr() instead of direct attribute access for st_birthtime, which is macOS-only and not in the Linux stat_result type stubs. Same runtime behavior, but now passes pyright strict on Linux CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
81c828b to
d181218
Compare
scan_fs.py runs on remote servers with Python 3.5+ where PEP 526 variable annotations are a SyntaxError. Exclude it from pyright checking and use type comments instead of inline annotations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
staging_path defaults to None in config, so an assert crashes at runtime in the E2E environment. Revert to type: ignore suppressions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/python/controller/controller.py (1)
44-66:⚠️ Potential issue | 🟠 MajorRecompute directory sizes after pruning excluded children.
Line 53 copies the original aggregate
file.size, but this helper can remove whole descendants. The returned directory then still counts bytes from excluded files, which can skew model diffs and any size-based state derived from the filtered tree.Suggested fix
- filtered = SystemFile( - name=file.name, - size=file.size, - is_dir=file.is_dir, - time_created=file.timestamp_created, - time_modified=file.timestamp_modified, - ) + filtered_children: list[SystemFile] = [] for child in file.children: if _matches_exclude(child.name, child.is_dir, patterns): continue # drop matched child (and its subtree) if child.is_dir: child = _filter_children(child, patterns) - filtered.add_child(child) + filtered_children.append(child) + filtered = SystemFile( + name=file.name, + size=sum(child.size for child in filtered_children), + is_dir=file.is_dir, + time_created=file.timestamp_created, + time_modified=file.timestamp_modified, + ) + for child in filtered_children: + filtered.add_child(child) return filtered🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/python/controller/controller.py` around lines 44 - 66, The helper _filter_children currently copies file.size into the new SystemFile and then prunes descendants, which leaves directory sizes stale; after recursively filtering children (in _filter_children) recompute the directory's aggregate size by summing sizes of filtered.children and assign that total to filtered.size (leave file.size unchanged for non-directories). Update the logic around filtered.add_child/_filter_children to ensure the recomputed size reflects removed descendants so callers of _filter_children see correct directory sizes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/controller/controller.py`:
- Around line 968-974: The block that iterates and processes shared
latest_extracted_results inside _update_pair_model_state causes duplicate
handling per pair; move that extraction-completion consumption logic out of
_update_pair_model_state and into __update_model (next to the validation
completion/failure handling) so each extraction result is processed exactly
once; update __update_model to iterate latest_extracted_results, perform the
persist sync and move-spawn checks there, and clear or mark consumed entries so
_update_pair_model_state no longer walks latest_extracted_results (keep
references to the same symbols: _update_pair_model_state, __update_model, and
latest_extracted_results).
In `@src/python/controller/validate/validate_process.py`:
- Around line 115-119: Change ValidateRequest.pair_id to allow None (type str |
None) so it matches how Controller constructs requests from _PairContext and
removes the need for type: ignore at call sites; update the __active_validations
key type from dict[tuple[str, str], ValidateRequest] to dict[tuple[str | None,
str], ValidateRequest] to reflect the actual (pair_id, request_id) key shape and
keep ValidateStatus/ValidateCompletedResult/ValidateFailedResult alignment with
the new optional pair_id.
In `@src/python/typings/tblib/pickling_support.pyi`:
- Around line 4-7: Change the type annotation for the get_locals parameter of
the install function so it accepts a callable receiving a frame object rather
than a TracebackType; update the signature in install to use a generic frame/Any
input and a simple dict return type (i.e., Callable[[Any], dict] | None) so
get_locals correctly types functions that take a frame; ensure you update the
import hints or typing references if needed and keep the rest of the install
signature unchanged.
In `@src/python/web/serialize/serialize_log_record.py`:
- Line 35: Add a unit test named something like test_serialize_message_with_args
that constructs a logging.LogRecord (or uses the same helper used in other
tests) with msg containing format placeholders (e.g. "%s %d" or "{}" depending
on formatter) and a non-empty args tuple, call SerializeLogRecord to produce the
json_dict, and assert that json_dict[SerializeLogRecord.__KEY_MESSAGE] equals
record.getMessage() (i.e. the fully formatted message). Ensure the test triggers
the same code path that uses record.getMessage() so the regression is covered.
In `@src/python/web/serialize/serialize_model.py`:
- Line 94: Replace the explicit generic constructor usage with a literal empty
list for readability: in the assignment to
json_dict[SerializeModel.__KEY_FILE_CHILDREN] (where json_dict is typed as
dict[str, Any]), change the initializer from list[dict[str, Any]]() to [] so the
code uses the idiomatic literal list form.
---
Outside diff comments:
In `@src/python/controller/controller.py`:
- Around line 44-66: The helper _filter_children currently copies file.size into
the new SystemFile and then prunes descendants, which leaves directory sizes
stale; after recursively filtering children (in _filter_children) recompute the
directory's aggregate size by summing sizes of filtered.children and assign that
total to filtered.size (leave file.size unchanged for non-directories). Update
the logic around filtered.add_child/_filter_children to ensure the recomputed
size reflects removed descendants so callers of _filter_children see correct
directory sizes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 93e0d87c-d46e-486e-88cc-0251fbfa6f98
📒 Files selected for processing (60)
src/python/common/__init__.pysrc/python/common/app_process.pysrc/python/common/config.pysrc/python/common/context.pysrc/python/common/multiprocessing_logger.pysrc/python/common/path_pairs_config.pysrc/python/common/status.pysrc/python/common/types.pysrc/python/controller/__init__.pysrc/python/controller/auto_queue.pysrc/python/controller/controller.pysrc/python/controller/controller_persist.pysrc/python/controller/delete/__init__.pysrc/python/controller/extract/__init__.pysrc/python/controller/extract/dispatch.pysrc/python/controller/extract/extract.pysrc/python/controller/extract/extract_process.pysrc/python/controller/model_builder.pysrc/python/controller/move/__init__.pysrc/python/controller/notifier.pysrc/python/controller/scan/__init__.pysrc/python/controller/scan/active_scanner.pysrc/python/controller/scan/scanner_process.pysrc/python/controller/validate/__init__.pysrc/python/controller/validate/validate_process.pysrc/python/lftp/__init__.pysrc/python/lftp/job_status.pysrc/python/lftp/job_status_parser.pysrc/python/lftp/lftp.pysrc/python/model/__init__.pysrc/python/model/diff.pysrc/python/model/file.pysrc/python/model/model.pysrc/python/pyrightconfig.jsonsrc/python/scan_fs.pysrc/python/seedsync.pysrc/python/ssh/__init__.pysrc/python/ssh/sshcp.pysrc/python/system/__init__.pysrc/python/system/file.pysrc/python/system/scanner.pysrc/python/typings/bottle/__init__.pyisrc/python/typings/pexpect/__init__.pyisrc/python/typings/pexpect/exceptions.pyisrc/python/typings/tblib/__init__.pyisrc/python/typings/tblib/pickling_support.pyisrc/python/web/__init__.pysrc/python/web/handler/logs.pysrc/python/web/handler/path_pairs.pysrc/python/web/handler/stream_log.pysrc/python/web/security.pysrc/python/web/serialize/__init__.pysrc/python/web/serialize/serialize_auto_queue.pysrc/python/web/serialize/serialize_config.pysrc/python/web/serialize/serialize_log_record.pysrc/python/web/serialize/serialize_model.pysrc/python/web/serialize/serialize_status.pysrc/python/web/utils.pysrc/python/web/web_app.pysrc/python/web/web_app_job.py
StreamQueue[LogRecord].__init__(self) resolves to _GenericAlias.__init__, not StreamQueue.__init__, so the internal queue was never created. This broke SSE config delivery to the frontend, causing all E2E settings/autoqueue/path-pairs tests to fail with empty/disabled fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix duplicate extraction-completion handling: move extraction result processing from _update_pair_model_state to __update_model so each result is consumed exactly once - Make ValidateRequest.pair_id accept None to match _PairContext.pair_id type, removing type: ignore at call sites - Fix tblib stub: get_locals takes a frame (Any), not TracebackType - Add test_serialize_message_with_args regression test for getMessage() - Use idiomatic [] literal instead of list[dict[str, Any]]() - Recompute directory sizes in _filter_children after pruning excluded children so filtered directories report accurate aggregate sizes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/controller/controller.py`:
- Around line 1347-1361: Add an assertion to narrow the type of
self.__context.config.controller.staging_path before computing staging_source so
Pyright can infer it's a str and you can remove the redundant "type: ignore"
comments; specifically, assert the staging_path is not None and is a str (e.g.
assert isinstance(self.__context.config.controller.staging_path, str)) before
using os.path.join to build staging_source and staging_file, then drop the
corresponding type: ignore annotations around staging_source, staging_file, and
the MoveProcess instantiation.
In `@src/python/web/handler/stream_log.py`:
- Line 34: Change the type annotation for the cached records to use typing.List
for repo consistency: update the annotation on self.__cached_records to
List[logging.LogRecord] and add or adjust the import from typing (e.g., ensure
List is imported) so the file uses the project-standard typing.List instead of
the built-in list generic; locate the attribute declaration in the class where
self.__cached_records is defined and update the annotation and imports
accordingly.
In `@src/python/web/serialize/serialize_model.py`:
- Line 71: Replace the dict() initialization with a literal to match the
idiomatic style used elsewhere: change the declaration of json_dict (json_dict:
dict[str, Any] = dict()) to use an empty dict literal {} so it becomes
json_dict: dict[str, Any] = {} for consistency with other literal
initializations (e.g., the empty list on line 94).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 046023a2-a02a-46d9-8e54-a6a0fa696ade
📒 Files selected for processing (6)
src/python/controller/controller.pysrc/python/controller/validate/validate_process.pysrc/python/tests/unittests/test_web/test_serialize/test_serialize_log_record.pysrc/python/typings/tblib/pickling_support.pyisrc/python/web/handler/stream_log.pysrc/python/web/serialize/serialize_model.py
- Add isinstance assert on staging_path in __spawn_move_process (all
callers already guard staging_path is truthy), removing 5 type: ignore
comments
- Use {} literal instead of dict() in serialize_model for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/controller/controller.py`:
- Around line 61-64: The code is replacing scanner-reported directory sizes by
summing kept_children into SystemFile.size (in the block constructing
SystemFile), which causes remote_size to diverge; instead preserve the original
scanner size and only prune children: when creating SystemFile (the constructor
call shown with name=file.name, size=...), set size=file.size (do not replace
with sum(c.size for c in kept_children)), and ensure child pruning logic only
updates the children list (kept_children) without mutating file.size;
references: _apply_exclude_patterns, SystemFile, file.is_dir, kept_children,
ModelBuilder/ModelFile.
- Around line 722-737: The extraction handler currently spawns a move as soon as
extraction finishes which races ongoing validation; change the block that
iterates latest_extracted_results to only call __spawn_move_process(result.name,
owner_pc) when no validation was queued for that pair+name. Implement this by
checking an existing validation-tracking mechanism (or add a small helper like
_is_validation_queued(pair_id, name)) before deciding to move: compute pkey via
_persist_key(result.pair_id, result.name), mark extracted as before, but only
call __spawn_move_process when _is_validation_queued(...) returns False;
otherwise leave the handoff to the validation completion/failure paths and still
call _sync_persist_to_all_builders() as before.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6afaccd8-540f-492d-b396-c3cd10f180f8
📒 Files selected for processing (6)
src/python/controller/controller.pysrc/python/controller/validate/validate_process.pysrc/python/tests/unittests/test_web/test_serialize/test_serialize_log_record.pysrc/python/typings/tblib/pickling_support.pyisrc/python/web/handler/stream_log.pysrc/python/web/serialize/serialize_model.py
Preserve scanner-reported directory sizes in _filter_children instead of recomputing from kept children, which caused remote_size to diverge. Add __pending_validation_keys tracking so extraction completion skips __spawn_move_process when validation is queued, deferring the move to the validation completion/failure paths that already call _spawn_deferred_move. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/python/web/handler/stream_log.py (1)
101-106:⚠️ Potential issue | 🟡 MinorGuard
loggerextraction fromkwargsbefore use.Line 104 assumes
kwargs["logger"]always exists and is alogging.Logger; misuse will raise a rawKeyError/AttributeError. Add a defensive type check for clearer failure behavior.Suggested patch
`@classmethod` `@overrides`(IStreamHandler) def register(cls, web_app: WebApp, **kwargs: Any) -> None: + logger = kwargs.get("logger") + if not isinstance(logger, logging.Logger): + raise TypeError("LogStreamHandler.register requires keyword argument 'logger: logging.Logger'") + # Initialize our cache when we register LogStreamHandler._cache = CachedQueueLogHandler(history_size_in_ms=LogStreamHandler._CACHE_HISTORY_SIZE_IN_MS) - kwargs["logger"].addHandler(LogStreamHandler._cache) + logger.addHandler(LogStreamHandler._cache) super().register(web_app=web_app, **kwargs)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/python/web/handler/stream_log.py` around lines 101 - 106, In register (class LogStreamHandler) guard extraction of kwargs["logger"] before using it: check that "logger" is present in kwargs and that its value is an instance of logging.Logger (or at least exposes addHandler), and if not, raise a clear TypeError/ValueError or use kwargs.get with a descriptive exception; then only call LogStreamHandler._cache = CachedQueueLogHandler(...) and kwargs["logger"].addHandler(...) after the check succeeds so you avoid raw KeyError/AttributeError and provide a clear failure message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/controller/controller.py`:
- Around line 45-50: The docstring for _filter_children incorrectly states that
directory sizes are recomputed from filtered children; update the docstring to
reflect the current behavior: the implementation preserves the original
file.size for directories (it does not recompute sizes) and only removes
excluded children/subtrees. Locate the _filter_children function in
controller.py and change the sentence about recomputing directory sizes to state
that directory sizes are preserved (or not modified) after filtering, keeping
any other descriptive text about child filtering intact.
- Around line 234-236: Change the post_callback parameter/attribute type in the
__init__ signature from Callable[..., Any] to Callable[[], None] so it
accurately reflects the zero-argument contract used when invoking
post_callback() (and update the attribute annotation accordingly); ensure the
call sites that supply callbacks—e.g., the bound method force_scan and the local
delete callback—conform to Callable[[], None] (add an explicit -> None return
annotation to local delete and add or tighten typing for force_scan so you can
remove the existing # type: ignore[assignment]).
---
Outside diff comments:
In `@src/python/web/handler/stream_log.py`:
- Around line 101-106: In register (class LogStreamHandler) guard extraction of
kwargs["logger"] before using it: check that "logger" is present in kwargs and
that its value is an instance of logging.Logger (or at least exposes
addHandler), and if not, raise a clear TypeError/ValueError or use kwargs.get
with a descriptive exception; then only call LogStreamHandler._cache =
CachedQueueLogHandler(...) and kwargs["logger"].addHandler(...) after the check
succeeds so you avoid raw KeyError/AttributeError and provide a clear failure
message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 788ef505-b140-498a-ba65-5fbc56e37642
📒 Files selected for processing (6)
src/python/controller/controller.pysrc/python/controller/validate/validate_process.pysrc/python/tests/unittests/test_web/test_serialize/test_serialize_log_record.pysrc/python/typings/tblib/pickling_support.pyisrc/python/web/handler/stream_log.pysrc/python/web/serialize/serialize_model.py
…e[[], None] Update docstring to reflect that directory sizes are preserved (not recomputed). Narrow CommandProcessWrapper.post_callback from Callable[..., Any] to Callable[[], None] matching the zero-argument call contract, removing the type: ignore on force_scan assignment. Add -> None annotation to force_scan. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoids reassigning post_callback variable which Pyright typed from the def in the DELETE_LOCAL branch, causing a type conflict in DELETE_REMOTE. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/python/controller/controller.py (1)
1222-1226: 🧹 Nitpick | 🔵 TrivialConsider removing the
Anytype hint on_pcparameter.The
_pc: Anyannotation defeats type checking. Since_pcis captured from the enclosingpcvariable (which is_PairContext), the annotation should be_PairContext:♻️ Suggested improvement
- def post_callback(delete_path: str = delete_path, _pc: Any = pc) -> None: + def post_callback(delete_path: str = delete_path, _pc: _PairContext = pc) -> None: _pc.local_scan_process.force_scan() if delete_path != _pc.local_path: _pc.active_scan_process.force_scan()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/python/controller/controller.py` around lines 1222 - 1226, The parameter annotation on post_callback currently uses _pc: Any even though _pc is captured from the enclosing pc (an _PairContext); change the annotation to _pc: _PairContext (i.e. def post_callback(delete_path: str = delete_path, _pc: _PairContext = pc) -> None) and ensure _PairContext is imported or forward-referenced (use a direct import or a typing-only import / TYPE_CHECKING guard or string-forward reference if needed) so the type checker sees the correct type instead of Any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/python/typings/tblib/pickling_support.pyi`:
- Line 6: Update the type of get_locals in the pickling_support.pyi stub:
replace the restrictive Callable[[Any], dict[str, object]] | None with a plain
Callable[[Any], dict] | None so the signature matches upstream tblib and allows
None/heterogeneous local values; modify the declaration for get_locals
accordingly to preserve the optional None default and keep the symbol name
get_locals unchanged.
---
Outside diff comments:
In `@src/python/controller/controller.py`:
- Around line 1222-1226: The parameter annotation on post_callback currently
uses _pc: Any even though _pc is captured from the enclosing pc (an
_PairContext); change the annotation to _pc: _PairContext (i.e. def
post_callback(delete_path: str = delete_path, _pc: _PairContext = pc) -> None)
and ensure _PairContext is imported or forward-referenced (use a direct import
or a typing-only import / TYPE_CHECKING guard or string-forward reference if
needed) so the type checker sees the correct type instead of Any.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a0a6507e-18be-4e21-b1b1-05f179b56485
📒 Files selected for processing (7)
src/python/controller/controller.pysrc/python/controller/scan/scanner_process.pysrc/python/controller/validate/validate_process.pysrc/python/tests/unittests/test_web/test_serialize/test_serialize_log_record.pysrc/python/typings/tblib/pickling_support.pyisrc/python/web/handler/stream_log.pysrc/python/web/serialize/serialize_model.py
|
|
||
| def install( | ||
| *exc_classes_or_instances: type[BaseException] | BaseException, | ||
| get_locals: Callable[[Any], dict[str, object]] | None = None, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider using plain dict for the get_locals return type to match the upstream library signature.
The current dict[str, object] is more restrictive than necessary—frame locals can contain None values, which object excludes in strict typing (you would need object | None). The previous review comment recommended Callable[[Any], dict] to align with tblib's documented signature. While this doesn't cause issues currently (since the only call site passes no arguments), using plain dict would be more accurate and future-proof.
📝 Suggested adjustment to match upstream signature
- get_locals: Callable[[Any], dict[str, object]] | None = None,
+ get_locals: Callable[[Any], dict] | None = None,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| get_locals: Callable[[Any], dict[str, object]] | None = None, | |
| def install( | |
| *exc_classes_or_instances: type[BaseException] | BaseException, | |
| get_locals: Callable[[Any], dict] | None = None, | |
| ) -> type[BaseException] | None: ... |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/python/typings/tblib/pickling_support.pyi` at line 6, Update the type of
get_locals in the pickling_support.pyi stub: replace the restrictive
Callable[[Any], dict[str, object]] | None with a plain Callable[[Any], dict] |
None so the signature matches upstream tblib and allows None/heterogeneous local
values; modify the declaration for get_locals accordingly to preserve the
optional None default and keep the symbol name get_locals unchanged.
Loosen pickling_support.pyi get_locals return type to plain dict to match upstream tblib. Replace _pc: Any with _pc: _PairContext in post_callback closure for proper type checking. Remove now-unused Any import. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eArgument Bare dict triggers reportMissingTypeArgument in Pyright strict mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
reportUnknown*family rules — these produce cascading noise from untyped third-party dependencies (bottle, pexpect, tblib), not from project codereportMissingTypeStubsandreportUnusedImport(conflicts with__init__.pyre-export pattern)What strict mode catches that basic didn't
reportMissingParameterTypereportMissingTypeArgumentlist[str], notlist)reportPrivateUsage_protectedaccess (ignored where intentional)reportUnnecessaryComparisonreportIncompatibleMethodOverrideDeferred rules (to enable later with type stubs)
The
reportUnknown*family (900+ errors) cascades from three untyped deps. These will be re-enabled as stubs become available:bottle— no official type stubspexpect— no official type stubstblib— no official type stubsTest plan
pyright— 0 errors in strict moderuff check— all checks passedruff format --check— all formattedCloses #291
🤖 Generated with Claude Code
Summary by CodeRabbit