Add a logger#680
Conversation
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR introduces a scverse-style logging system for rapids-singlecell with Pydantic settings-based verbosity control. It adds a new ChangesLogging infrastructure and instrumentation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 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 docstrings
🧪 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: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/rapids_singlecell/logging.py`:
- Around line 104-116: The code currently uses truthiness checks on optional
formatter fields which skips valid falsy values (e.g., timedelta(0)); update the
conditionals to explicit None checks: replace "if record.time_passed:" with "if
record.time_passed is not None:" and keep the microseconds branch as an explicit
check (e.g., "if record.time_passed.microseconds != 0:" or similar) so
zero-duration is handled, and replace "if record.deep:" with "if record.deep is
not None:"; ensure the logic inside (replacing "{time_passed}" in record.msg,
adjusting self._style._fmt, and appending ": {deep}" behavior) remains unchanged
apart from these None checks.
In `@src/rapids_singlecell/preprocessing/_neighbors/__init__.py`:
- Around line 190-193: The auto-adjustment block that sets n_neighbors when
n_neighbors > adata.shape[0] needs an explicit guard for empty AnnData: check if
adata.shape[0] == 0 before adjusting and raise a clear ValueError (or log and
exit) instead of proceeding to set n_neighbors to 1; update the code around the
n_neighbors adjustment (the block that references adata.shape and n_neighbors in
src/rapids_singlecell/preprocessing/_neighbors/__init__.py) so empty input is
detected early and handled with an informative error message to avoid later KNN
failures.
🪄 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: CHILL
Plan: Pro
Run ID: ce41bad9-ff8d-4e9c-ac3c-2dbc9a4ddc78
📒 Files selected for processing (20)
src/rapids_singlecell/__init__.pysrc/rapids_singlecell/_settings/__init__.pysrc/rapids_singlecell/_settings/verbosity.pysrc/rapids_singlecell/decoupler_gpu/_helper/_log.pysrc/rapids_singlecell/logging.pysrc/rapids_singlecell/preprocessing/_harmony_integrate.pysrc/rapids_singlecell/preprocessing/_hvg/__init__.pysrc/rapids_singlecell/preprocessing/_neighbors/__init__.pysrc/rapids_singlecell/preprocessing/_normalize.pysrc/rapids_singlecell/preprocessing/_pca.pysrc/rapids_singlecell/preprocessing/_regress_out.pysrc/rapids_singlecell/preprocessing/_scale.pysrc/rapids_singlecell/preprocessing/_scrublet/__init__.pysrc/rapids_singlecell/preprocessing/_scrublet/core.pysrc/rapids_singlecell/preprocessing/_simple.pysrc/rapids_singlecell/tools/_clustering.pysrc/rapids_singlecell/tools/_rank_genes_groups/__init__.pysrc/rapids_singlecell/tools/_tsne.pysrc/rapids_singlecell/tools/_umap.pytests/test_logging.py
| if record.time_passed: | ||
| if record.time_passed.microseconds: | ||
| record.time_passed = timedelta( | ||
| seconds=int(record.time_passed.total_seconds()) | ||
| ) | ||
| if "{time_passed}" in record.msg: | ||
| record.msg = record.msg.replace( | ||
| "{time_passed}", str(record.time_passed) | ||
| ) | ||
| else: | ||
| self._style._fmt += " ({time_passed})" | ||
| if record.deep: | ||
| record.msg = f"{record.msg}: {record.deep}" |
There was a problem hiding this comment.
Use explicit None checks for optional formatter fields.
Please avoid truthiness checks on optional values here. This also fixes the zero-duration edge case (timedelta(0)) being skipped.
Suggested patch
- if record.time_passed:
+ if record.time_passed is not None:
if record.time_passed.microseconds:
record.time_passed = timedelta(
seconds=int(record.time_passed.total_seconds())
)
if "{time_passed}" in record.msg:
record.msg = record.msg.replace(
"{time_passed}", str(record.time_passed)
)
else:
self._style._fmt += " ({time_passed})"
- if record.deep:
+ if record.deep is not None:
record.msg = f"{record.msg}: {record.deep}"📝 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.
| if record.time_passed: | |
| if record.time_passed.microseconds: | |
| record.time_passed = timedelta( | |
| seconds=int(record.time_passed.total_seconds()) | |
| ) | |
| if "{time_passed}" in record.msg: | |
| record.msg = record.msg.replace( | |
| "{time_passed}", str(record.time_passed) | |
| ) | |
| else: | |
| self._style._fmt += " ({time_passed})" | |
| if record.deep: | |
| record.msg = f"{record.msg}: {record.deep}" | |
| if record.time_passed is not None: | |
| if record.time_passed.microseconds: | |
| record.time_passed = timedelta( | |
| seconds=int(record.time_passed.total_seconds()) | |
| ) | |
| if "{time_passed}" in record.msg: | |
| record.msg = record.msg.replace( | |
| "{time_passed}", str(record.time_passed) | |
| ) | |
| else: | |
| self._style._fmt += " ({time_passed})" | |
| if record.deep is not None: | |
| record.msg = f"{record.msg}: {record.deep}" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/rapids_singlecell/logging.py` around lines 104 - 116, The code currently
uses truthiness checks on optional formatter fields which skips valid falsy
values (e.g., timedelta(0)); update the conditionals to explicit None checks:
replace "if record.time_passed:" with "if record.time_passed is not None:" and
keep the microseconds branch as an explicit check (e.g., "if
record.time_passed.microseconds != 0:" or similar) so zero-duration is handled,
and replace "if record.deep:" with "if record.deep is not None:"; ensure the
logic inside (replacing "{time_passed}" in record.msg, adjusting
self._style._fmt, and appending ": {deep}" behavior) remains unchanged apart
from these None checks.
| if n_neighbors > adata.shape[0]: | ||
| n_neighbors = 1 + int(0.5 * adata.shape[0]) | ||
| logg.warning(f"n_obs too small: adjusting to `n_neighbors = {n_neighbors}`") | ||
|
|
There was a problem hiding this comment.
Guard empty AnnData before neighbor auto-adjustment.
When adata.shape[0] == 0, the new adjustment produces n_neighbors = 1, which is still invalid and fails later in KNN with a less direct error. Add an explicit early check for empty input.
💡 Proposed fix
+ if adata.shape[0] == 0:
+ raise ValueError("Cannot compute neighbors on empty AnnData (`n_obs == 0`).")
+
if n_neighbors > adata.shape[0]:
n_neighbors = 1 + int(0.5 * adata.shape[0])
logg.warning(f"n_obs too small: adjusting to `n_neighbors = {n_neighbors}`")📝 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.
| if n_neighbors > adata.shape[0]: | |
| n_neighbors = 1 + int(0.5 * adata.shape[0]) | |
| logg.warning(f"n_obs too small: adjusting to `n_neighbors = {n_neighbors}`") | |
| if adata.shape[0] == 0: | |
| raise ValueError("Cannot compute neighbors on empty AnnData (`n_obs == 0`).") | |
| if n_neighbors > adata.shape[0]: | |
| n_neighbors = 1 + int(0.5 * adata.shape[0]) | |
| logg.warning(f"n_obs too small: adjusting to `n_neighbors = {n_neighbors}`") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/rapids_singlecell/preprocessing/_neighbors/__init__.py` around lines 190
- 193, The auto-adjustment block that sets n_neighbors when n_neighbors >
adata.shape[0] needs an explicit guard for empty AnnData: check if
adata.shape[0] == 0 before adjusting and raise a clear ValueError (or log and
exit) instead of proceeding to set n_neighbors to 1; update the code around the
n_neighbors adjustment (the block that references adata.shape and n_neighbors in
src/rapids_singlecell/preprocessing/_neighbors/__init__.py) so empty input is
detected early and handled with an informative error message to avoid later KNN
failures.
|
This adds a scanpy style logger to rsc.