Skip to content

Conversation

@katosh
Copy link
Collaborator

@katosh katosh commented Nov 30, 2025

Update the main branch for release v0.6.2:

  • fix differential expression analysis using groups
  • increase testing coverage
  • thread and GPU-usage control in CLI
  • fix volcano_de plot when the layer is None

Summary by CodeRabbit

Release Notes v0.6.2

  • New Features

    • Added GPU acceleration support via --use-gpu flag for CLI commands.
    • Added thread control via --threads flag to limit compute threads for JAX, NumPy, and Dask.
    • Improved group-wise FDR analysis with null genes handling in differential expression.
  • Bug Fixes

    • Fixed volcano plot visualization when layer is None.
    • Fixed differential expression analysis using groups with null genes.
  • Documentation

    • Added CLI documentation for GPU and threading compute options.
  • Chores

    • Version bumped to 0.6.2.
    • GitHub Actions workflow expanded to run on dev and main branches.
    • Enhanced test infrastructure and fixtures.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Walkthrough

Release 0.6.2 introduces compute configuration system for GPU/thread control in CLI, extends differential expression with group-wise FDR statistics, fixes layer inference null-check bug, significantly expands test coverage with new test modules and fixtures, optimizes test data sizes, and updates documentation with compute options.

Changes

Cohort / File(s) Summary
Version & Release
kompot/version.py, pyproject.toml, CHANGELOG.md
Version bumped to 0.6.2; pytest configuration updated with markers and warning filters; CHANGELOG entry added for new features and fixes
Compute Configuration System
kompot/cli/compute_config.py
New module providing configure_compute(), _configure_thread_limits(), _configure_jax(), _configure_dask(), get_device_info(), and log_compute_environment() to centralize GPU/thread/backend configuration
CLI Integration - DE & DA
kompot/cli/de.py, kompot/cli/da.py
Added --use-gpu and --threads options to DE and DA parsers; integrated early compute configuration via configure_compute() after mellon import
CLI Main Entry
kompot/cli/main.py
Added _set_early_thread_limits() to parse --threads before NumPy import; deferred subcommand module imports until after environment setup
Differential Expression Enhancement
kompot/anndata/differential_expression.py
Added group-wise FDR handling with null genes: new varm entries for mahalanobis_local_fdr_key_groups, is_de_groups, ptp_groups; per-group statistics computation and logging
Bug Fix - Plot Layer Inference
kompot/plot/expression.py
Added None-check before substring match on inferred_layer to prevent TypeError when layer is None
CLI Documentation
docs/source/cli.rst
Added "Compute Options" sections under Differential Expression and Differential Abundance documenting --use-gpu and --threads flags
Test Infrastructure & Fixtures
tests/conftest.py
New comprehensive pytest configuration with CPU-only JAX setup, custom markers (slow, integration, memory), and AnnData/parameter fixtures (tiny/small/medium_adata, fast/integration_*_params, temp_dir)
Compute Configuration Tests
tests/test_cli_compute_config.py
New module with extensive unit tests for thread/device configuration, JAX/Dask setup, CLI main early thread handling, and DE/DA CLI workflows with mocked compute functions
Advanced DE Feature Tests
tests/test_de_advanced_features.py
New comprehensive test suite covering FDR edge cases, DA+DE integration, groups functionality, additional statistics, custom result keys, and robustness scenarios
New Comprehensive Test Modules
tests/test_cli_dm_coverage.py, tests/test_field_tracking_coverage.py, tests/test_groupwise_fdr_integration.py, tests/test_heatmap_visualization_coverage.py, tests/test_plot_expression_coverage.py, tests/test_runinfo_coverage.py
New test modules providing extensive coverage for DM CLI, field tracking utilities, group-wise FDR integration, heatmap visualization helpers, plot expression functionality, and RunInfo/RunComparison classes
Test Data Size Optimization
tests/test_anndata_*.py, tests/test_cleanup*.py, tests/test_de_*.py, tests/test_direction_plot.py, tests/test_fdr_*.py, tests/test_html_representation.py, tests/test_plot_functions.py, tests/test_posterior_covariance.py, tests/test_ptp_functionality.py, tests/test_single_condition_variance.py, tests/test_store_additional_stats.py, tests/test_volcano_*.py
Reduced default n_cells from 100 to 60 across all test helper functions; adjusted tolerances and per-condition distributions where applicable
Test Data Logic Updates
tests/test_anndata_groups.py, tests/test_de_comprehensive_params.py
Updated mahalanobis key detection to exclude "fdr"-containing keys; reduced n_cells and n_landmarks defaults for faster test execution
Workflow Configuration
.github/workflows/tests.yml
Expanded GitHub Actions tests workflow to run on both main and dev branches

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Entry (main.py)
    participant Parser as Parser
    participant Env as Environment
    participant Mellon as Mellon (import)
    participant Compute as Compute Config
    participant JAX as JAX Backend
    participant NumPy as NumPy/Dask
    participant DE as Differential Expression

    CLI->>CLI: _set_early_thread_limits()
    CLI->>Env: Set OMP/MKL/BLAS env vars
    CLI->>Parser: Parse --threads, --use-gpu
    Parser->>CLI: Return args
    CLI->>Mellon: Import mellon (triggers JAX init)
    Mellon->>JAX: JAX initialization
    CLI->>Compute: configure_compute(use_gpu, n_threads)
    Compute->>Env: _configure_thread_limits()
    Env->>NumPy: Set thread limits
    Compute->>JAX: _configure_jax(use_gpu, n_threads)
    JAX->>JAX: Set XLA_FLAGS, platform
    Compute->>NumPy: _configure_dask() (optional)
    NumPy->>NumPy: Configure Dask scheduler
    Compute->>Compute: log_compute_environment()
    CLI->>DE: run_de(adata, params)
    DE->>DE: compute_differential_expression()
    DE->>DE: Generate group-wise FDR stats
    DE->>DE: Store varm entries (local_fdr_groups, is_de_groups, ptp_groups)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • kompot/cli/compute_config.py: New module with multiple interdependent configuration functions (JAX, NumPy, Dask); careful review needed for environment variable handling, ordering constraints (threads before JAX), and mellon/optional dependency interactions.
  • kompot/anndata/differential_expression.py: Substantial logic additions for group-wise FDR handling with null genes; requires verification of varm key naming consistency, per-group statistics correctness, and interaction with existing subset/expanded_genes logic.
  • kompot/cli/de.py & kompot/cli/da.py: CLI option integration requires verification of argument parsing, exclusion from downstream params dict, and error handling for configure_compute failures.
  • tests/test_cli_compute_config.py: Large new test file with complex mocking; verify mock strategies and all CLI/config code paths are exercised without false positives.
  • tests/test_de_advanced_features.py & tests/test_groupwise_fdr_integration.py: Extensive test suites with many interdependent scenarios; validate test logic and fixture behavior across all edge cases.
  • Distributed test n_cells reductions: While individually simple, the widespread nature of this change (25+ files) warrants spot-checking for consistency and verifying no unintended test failures from smaller datasets.

Poem

🐰 With GPU threads now under control,
And groups with FDR statistics whole,
The tests run swift with datasets lean,
The finest diff we've ever seen!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'v0.6.2' is vague and generic, using only a version number without describing the actual changes made in the pull request. Replace with a more descriptive title that summarizes the main changes, such as 'Release v0.6.2: Add CLI compute configuration and fix group-wise FDR handling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description covers the main objectives but lacks detailed information about specific changes, testing approach, and other template sections like related issues and type of change.
Docstring Coverage ✅ Passed Docstring coverage is 95.54% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
kompot/cli/main.py (1)

67-71: Version string mismatch.

The version string shows 0.6.0 but this PR is for release v0.6.2. This should be updated to match the release version.

     parser.add_argument(
         '--version',
         action='version',
-        version='%(prog)s 0.6.0'
+        version='%(prog)s 0.6.2'
     )
🧹 Nitpick comments (23)
CHANGELOG.md (1)

5-11: LGTM! Consider fixing list indentation.

The CHANGELOG entry accurately documents the v0.6.2 changes. However, the bullet points have inconsistent indentation.

Apply this diff to align with markdown style guidelines:

 ## [0.6.2]
 
- - fix differential expression analysis using `groups`
- - increase testing coverage
- - thread and GPU-usage control in CLI
- - fix `volcano_de` plot when the layer is `None`
+- fix differential expression analysis using `groups`
+- increase testing coverage
+- thread and GPU-usage control in CLI
+- fix `volcano_de` plot when the layer is `None`

Based on static analysis hints from markdownlint-cli2.

pyproject.toml (1)

13-28: Pytest configuration and version bump look consistent; consider narrowing warning filters

The updated pytest options, markers, and the 0.6.2 version bump are consistent with the rest of the PR. The broad filterwarnings rules that ignore all DeprecationWarning/FutureWarning classes will keep test output clean but may also hide important upstream deprecations; if you rely on these to catch API changes, consider narrowing the filters to specific modules/messages instead of silencing the entire categories.

Also applies to: 40-40

kompot/anndata/differential_expression.py (1)

2616-2631: Minor: Redundant truncation logic for group ptp values.

Lines 2621-2625 have overlapping conditions - after checking for expanded_genes length, the elif also truncates for any length mismatch with selected_genes. While this is defensive, consider adding a debug log when hitting the fallback truncation to aid troubleshooting unexpected array sizes.

                     if len(subset_ptp) == len(expanded_genes):
                         subset_ptp = subset_ptp[:len(selected_genes)]
                     elif len(subset_ptp) != len(selected_genes):
+                        logger.debug(
+                            f"Group '{subset_name}' ptp length {len(subset_ptp)} unexpected, truncating to {len(selected_genes)}"
+                        )
                         # Truncate if too long
                         subset_ptp = subset_ptp[:len(selected_genes)]
tests/test_groupwise_fdr_integration.py (1)

386-392: Consider adding more specific consistency assertions.

The test validates that DE counts are non-negative, but the comment notes this is a "soft check." Consider adding a more specific test that verifies at least some overlap between globally DE genes and group-wise DE genes when the signal is strong, to catch potential bugs where group results are completely independent of global results.

kompot/cli/da.py (1)

259-266: Consider catching more specific exceptions.

The static analysis flagged the blind Exception catch (BLE001). While the graceful fallback is appropriate, this could inadvertently suppress critical errors. Consider catching more specific exceptions.

     try:
         # Import mellon to trigger its JAX configuration
         import mellon
         # Now configure our settings (will override mellon's CPU-only default if needed)
         configure_compute(use_gpu=use_gpu, n_threads=n_threads)
-    except Exception as e:
+    except (ImportError, RuntimeError, OSError) as e:
         logger.warning(f"Could not configure compute backend: {e}")
         logger.warning("Proceeding with default configuration")
tests/test_runinfo_coverage.py (4)

122-124: Use raw string for regex pattern in match=.

The pattern "Run ID .* not found" contains regex metacharacters (.*). Use a raw string to make the intent explicit and prevent potential issues with escape sequences.

     def test_init_invalid_run_id(self, adata_with_de_history):
         """Test that invalid run_id raises error."""
-        with pytest.raises(ValueError, match="Run ID .* not found"):
+        with pytest.raises(ValueError, match=r"Run ID .* not found"):
             RunInfo(adata_with_de_history, run_id=999)

294-297: Avoid equality comparison to True; use truthiness check.

Per PEP 8, use if summary['has_groups']: for boolean checks rather than == True.

         summary = run_info.get_summary()
-        assert summary['has_groups'] == True
+        assert summary['has_groups'] is True
         assert summary['groups_count'] == 5
         assert 'groups' in summary

Alternatively, if only truthiness matters:

assert summary['has_groups']

451-456: Rename unused loop variable key to _key.

The loop variable key is not used within the loop body.

         # Check that 'different' contains dicts with 'run1' and 'run2' keys
-        for key, value in param_comp['different'].items():
+        for _key, value in param_comp['different'].items():
             assert isinstance(value, dict)
             assert 'run1' in value
             assert 'run2' in value

489-496: Rename unused loop variable location to _location.

The loop variable location is not used within the loop body.

         # Each location should have same, only_in_run1, only_in_run2
-        for location, data in by_location.items():
+        for _location, data in by_location.items():
             assert 'same' in data
             assert 'only_in_run1' in data
             assert 'only_in_run2' in data
kompot/cli/de.py (2)

276-284: Consider catching more specific exceptions or logging the exception type.

While catching broad Exception for graceful degradation is reasonable here (as configuration failure shouldn't halt analysis), consider logging the exception class to aid debugging.

     try:
         # Import mellon to trigger its JAX configuration
         import mellon
         # Now configure our settings (will override mellon's CPU-only default if needed)
         configure_compute(use_gpu=use_gpu, n_threads=n_threads)
     except Exception as e:
-        logger.warning(f"Could not configure compute backend: {e}")
+        logger.warning(f"Could not configure compute backend ({type(e).__name__}): {e}")
         logger.warning("Proceeding with default configuration")

230-250: Redundant dictionary operations can be simplified.

Several lines perform args_dict['key'] = args_dict.pop('key') which is a no-op. These appear to be remnants from when CLI arg names differed from function parameter names.

     # Rename CLI args to match function parameters
-    if 'obsm_key' in args_dict:
-        args_dict['obsm_key'] = args_dict.pop('obsm_key')
-    if 'result_key' in args_dict:
-        args_dict['result_key'] = args_dict.pop('result_key')
-    if 'n_landmarks' in args_dict:
-        args_dict['n_landmarks'] = args_dict.pop('n_landmarks')
-    if 'sample_col' in args_dict:
-        args_dict['sample_col'] = args_dict.pop('sample_col')
-    if 'batch_size' in args_dict:
-        args_dict['batch_size'] = args_dict.pop('batch_size')
-    if 'fdr_threshold' in args_dict:
-        args_dict['fdr_threshold'] = args_dict.pop('fdr_threshold')
-    if 'null_genes' in args_dict:
-        args_dict['null_genes'] = args_dict.pop('null_genes')
-    if 'store_landmarks' in args_dict:
-        args_dict['store_landmarks'] = args_dict.pop('store_landmarks')
-    if 'store_additional_stats' in args_dict:
-        args_dict['store_additional_stats'] = args_dict.pop('store_additional_stats')
+    # Handle special CLI arg transformations
     if 'no_progress' in args_dict:
         args_dict['progress'] = not args_dict.pop('no_progress')

The only meaningful transformation is no_progressprogress. The other operations don't change anything.

tests/test_field_tracking_coverage.py (2)

349-358: Prefix unused unpacked variable with underscore.

The prev_run variable is unpacked but never used. Prefix with _ to indicate intentional discard.

-        will_overwrite, overwritten, prev_run = detect_output_field_overwrite(
+        will_overwrite, overwritten, _prev_run = detect_output_field_overwrite(
             sample_adata,
             analysis_type="da",
             field_names=field_names,
             overwrite=False
         )

This pattern applies to similar occurrences at lines 372, 394, 416, 431, 447, and 461.


443-456: Multiple unused unpacked variables in this test.

Both overwritten and prev_run are unpacked but never used.

-        will_overwrite, overwritten, prev_run = detect_output_field_overwrite(
+        will_overwrite, _overwritten, _prev_run = detect_output_field_overwrite(
             sample_adata,
             result_type="differential_abundance",
             output_patterns=['field1'],
             overwrite=False,
             location="obs"
         )
kompot/cli/compute_config.py (2)

23-54: Use Optional[int] for type hints with None default.

PEP 484 prohibits implicit Optional. The parameter n_threads: int = None should be n_threads: Optional[int] = None.

+from typing import Optional
+
-def configure_compute(use_gpu: bool = False, n_threads: int = None):
+def configure_compute(use_gpu: bool = False, n_threads: Optional[int] = None):

This also applies to _configure_jax (line 108) and _configure_dask (line 169).


126-131: Remove extraneous f prefix from string without placeholders.

             if len(devices) > 0:
-                logger.info(f"  JAX: GPU mode enabled")
+                logger.info("  JAX: GPU mode enabled")
                 logger.info(f"    Available GPU devices: {len(devices)}")
tests/test_cli_dm_coverage.py (2)

280-306: Remove dead code in skipped test.

This test contains unreachable code (lines 293-301) because it's always skipped at line 302. Either implement the test properly or remove the dead code to avoid confusion.

 class TestDMRunOutputFormat:
     """Test run_dm output format handling."""

     def test_run_dm_unsupported_output_format(self, sample_adata_with_pca, tmp_path):
         """Test run_dm with unsupported output format."""
-        from kompot.cli.dm import run_dm
-        import argparse
-
-        # Save sample data
-        input_file = tmp_path / 'input.h5ad'
-        sample_adata_with_pca.write_h5ad(input_file)
-
-        # Mock palantir.utils.run_diffusion_maps
-        try:
-            import palantir
-
-            def mock_run_dm(adata, **kwargs):
-                # Add DM_EigenVectors to adata
-                adata.obsm['DM_EigenVectors'] = np.random.randn(adata.n_obs, 10)
-
-            import kompot.cli.dm
-            original_palantir = sys.modules.get('palantir')
-
-            # Monkeypatch is tricky here, let's just test the parser instead
-            # This test would require more complex mocking
-            pytest.skip("Requires complex palantir mocking")
-
-        except ImportError:
-            pytest.skip("Palantir not installed")
+        # TODO: Implement test for unsupported output format
+        # Requires complex palantir mocking
+        pytest.skip("Requires complex palantir mocking")

328-362: Unused variable args in skipped test.

The args variable is assigned but never used because the test is always skipped. Either implement the test or simplify to just the skip statement.

tests/test_cli_compute_config.py (2)

55-70: Improve exception handling in JAX configuration test.

The broad except Exception: pass silently swallows all errors, making debugging difficult. Consider catching specific exceptions or at least logging the error.

     def test_configure_jax_cpu(self, monkeypatch):
         """Test JAX configuration for CPU."""
         from kompot.cli.compute_config import _configure_jax
         import jax

         _configure_jax(use_gpu=False, n_threads=None)

         # Check that JAX is configured for CPU
         # Note: This test may vary depending on JAX installation
         try:
             platform = jax.devices()[0].platform
             # Should be 'cpu' but might be different depending on environment
             assert platform in ['cpu', 'gpu']
-        except Exception:
-            # If JAX is not properly configured, that's OK for this test
-            pass
+        except (RuntimeError, IndexError) as e:
+            # JAX may not be properly configured in all environments
+            pytest.skip(f"JAX not properly configured: {e}")

198-240: Remove unused capsys fixture.

The capsys fixture is declared but never used in this test method.

-    def test_run_de_missing_output(self, sample_adata_for_cli, tmp_path, capsys):
+    def test_run_de_missing_output(self, sample_adata_for_cli, tmp_path):
tests/test_plot_expression_coverage.py (3)

130-136: Use raw string for regex pattern.

The pattern contains . which is a regex metacharacter. Use a raw string to make the intent clearer and avoid potential issues.

     def test_missing_gene_error(self):
         """Test that ValueError is raised for missing gene."""
-        with pytest.raises(ValueError, match="not found in adata.var_names"):
+        with pytest.raises(ValueError, match=r"not found in adata\.var_names"):
             plot_gene_expression(
                 self.adata,
                 gene='NONEXISTENT_GENE'
             )

202-219: Consider using pytest.importorskip or conditional assertions.

The try/except Exception: pass pattern makes test failures silent. While acceptable for coverage tests with optional dependencies, consider using pytest.importorskip('scanpy') or wrapping assertions in conditionals for clearer test behavior.


214-216: Prefix unused variable with underscore.

The unpacked axs variable is unused. Use _axs or _ to indicate it's intentionally unused.

             if result is not None:
-                fig, axs = result
+                fig, _axs = result
                 assert fig is not None
                 plt.close(fig)

This pattern appears multiple times in the file (lines 232, 250, 268, 285, 303, 413, 444, 496, 520, 584, 600).

tests/test_heatmap_visualization_coverage.py (1)

72-75: Use callable() instead of hasattr(x, "__call__").

hasattr(x, "__call__") is unreliable for testing callability. Use callable() for consistent results.

         # Should return a colormap object
         assert cmap_obj is not None
-        assert hasattr(cmap_obj, '__call__')  # Colormap is callable
+        assert callable(cmap_obj)  # Colormap is callable
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca0e3d and 596e57e.

📒 Files selected for processing (40)
  • .github/workflows/tests.yml (1 hunks)
  • CHANGELOG.md (1 hunks)
  • docs/source/cli.rst (2 hunks)
  • kompot/anndata/differential_expression.py (5 hunks)
  • kompot/cli/compute_config.py (1 hunks)
  • kompot/cli/da.py (4 hunks)
  • kompot/cli/de.py (4 hunks)
  • kompot/cli/main.py (1 hunks)
  • kompot/plot/expression.py (1 hunks)
  • kompot/version.py (1 hunks)
  • pyproject.toml (2 hunks)
  • tests/conftest.py (1 hunks)
  • tests/test_anndata_differential_abundance.py (1 hunks)
  • tests/test_anndata_groups.py (2 hunks)
  • tests/test_anndata_representation_check.py (8 hunks)
  • tests/test_cleanup.py (1 hunks)
  • tests/test_cleanup_multiple_runs.py (1 hunks)
  • tests/test_cli_compute_config.py (1 hunks)
  • tests/test_cli_dm_coverage.py (1 hunks)
  • tests/test_de_advanced_features.py (1 hunks)
  • tests/test_de_comprehensive_params.py (25 hunks)
  • tests/test_de_error_handling.py (1 hunks)
  • tests/test_direction_plot.py (1 hunks)
  • tests/test_fdr_edge_cases.py (13 hunks)
  • tests/test_fdr_integration.py (1 hunks)
  • tests/test_field_tracking_coverage.py (1 hunks)
  • tests/test_groupwise_fdr_integration.py (1 hunks)
  • tests/test_heatmap_visualization_coverage.py (1 hunks)
  • tests/test_html_representation.py (1 hunks)
  • tests/test_plot_expression_coverage.py (1 hunks)
  • tests/test_plot_functions.py (1 hunks)
  • tests/test_posterior_covariance.py (1 hunks)
  • tests/test_ptp_functionality.py (1 hunks)
  • tests/test_runinfo_coverage.py (1 hunks)
  • tests/test_single_condition_variance.py (5 hunks)
  • tests/test_store_additional_stats.py (1 hunks)
  • tests/test_volcano_de.py (1 hunks)
  • tests/test_volcano_de_edge_cases.py (1 hunks)
  • tests/test_volcano_de_fdr.py (1 hunks)
  • tests/test_volcano_multi_da.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (14)
tests/test_field_tracking_coverage.py (1)
kompot/anndata/utils/field_tracking.py (8)
  • get_run_history (14-74)
  • append_to_run_history (77-108)
  • get_last_run_info (111-128)
  • generate_output_field_names (131-272)
  • get_environment_info (275-331)
  • detect_output_field_overwrite (334-460)
  • validate_field_run_id (482-538)
  • get_run_from_history (541-668)
tests/test_plot_expression_coverage.py (1)
kompot/plot/expression.py (2)
  • plot_gene_expression (83-642)
  • _infer_expression_keys (18-74)
tests/test_runinfo_coverage.py (1)
kompot/anndata/utils/runinfo.py (6)
  • RunInfo (16-852)
  • RunComparison (855-1593)
  • _get_fields_for_run (121-185)
  • get_raw_data (440-449)
  • get_data (346-392)
  • compare_with (330-344)
tests/test_volcano_multi_da.py (8)
tests/test_anndata_groups.py (1)
  • create_test_anndata (44-105)
tests/test_anndata_differential_abundance.py (1)
  • create_test_anndata (15-50)
tests/test_direction_plot.py (1)
  • create_test_anndata (17-48)
tests/test_html_representation.py (1)
  • create_test_anndata (11-37)
tests/test_plot_functions.py (1)
  • create_test_anndata (18-50)
tests/test_posterior_covariance.py (1)
  • create_test_anndata (12-61)
tests/test_volcano_de.py (1)
  • create_test_anndata (17-91)
tests/test_anndata_functions.py (1)
  • create_test_anndata (13-59)
tests/test_html_representation.py (8)
tests/test_anndata_groups.py (1)
  • create_test_anndata (44-105)
tests/test_anndata_differential_abundance.py (1)
  • create_test_anndata (15-50)
tests/test_direction_plot.py (1)
  • create_test_anndata (17-48)
tests/test_plot_functions.py (1)
  • create_test_anndata (18-50)
tests/test_posterior_covariance.py (1)
  • create_test_anndata (12-61)
tests/test_volcano_de.py (1)
  • create_test_anndata (17-91)
tests/test_volcano_multi_da.py (1)
  • create_test_anndata (17-48)
tests/test_anndata_functions.py (1)
  • create_test_anndata (13-59)
tests/test_plot_functions.py (8)
tests/test_anndata_groups.py (1)
  • create_test_anndata (44-105)
tests/test_anndata_differential_abundance.py (1)
  • create_test_anndata (15-50)
tests/test_direction_plot.py (1)
  • create_test_anndata (17-48)
tests/test_html_representation.py (1)
  • create_test_anndata (11-37)
tests/test_posterior_covariance.py (1)
  • create_test_anndata (12-61)
tests/test_volcano_de.py (1)
  • create_test_anndata (17-91)
tests/test_volcano_multi_da.py (1)
  • create_test_anndata (17-48)
tests/test_anndata_functions.py (1)
  • create_test_anndata (13-59)
tests/test_heatmap_visualization_coverage.py (1)
kompot/plot/heatmap/visualization.py (3)
  • _draw_diagonal_split_cell (72-193)
  • _draw_split_dot_cell (196-378)
  • _draw_fold_change_cell (380-458)
kompot/cli/da.py (1)
kompot/cli/compute_config.py (1)
  • configure_compute (23-75)
kompot/cli/de.py (1)
kompot/cli/compute_config.py (1)
  • configure_compute (23-75)
kompot/anndata/differential_expression.py (1)
kompot/anndata/fdr_utils.py (1)
  • compute_fdr_statistics (124-219)
tests/test_groupwise_fdr_integration.py (1)
kompot/anndata/differential_expression.py (1)
  • compute_differential_expression (277-2858)
tests/test_de_error_handling.py (1)
tests/test_group_utils_comprehensive.py (1)
  • create_test_adata (15-39)
tests/test_anndata_differential_abundance.py (8)
tests/test_anndata_groups.py (1)
  • create_test_anndata (44-105)
tests/test_direction_plot.py (1)
  • create_test_anndata (17-48)
tests/test_html_representation.py (1)
  • create_test_anndata (11-37)
tests/test_plot_functions.py (1)
  • create_test_anndata (18-50)
tests/test_posterior_covariance.py (1)
  • create_test_anndata (12-61)
tests/test_volcano_de.py (1)
  • create_test_anndata (17-91)
tests/test_volcano_multi_da.py (1)
  • create_test_anndata (17-48)
tests/test_anndata_functions.py (1)
  • create_test_anndata (13-59)
kompot/cli/main.py (1)
kompot/cli/utils.py (1)
  • setup_logging (98-112)
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md

7-7: Unordered list indentation
Expected: 0; Actual: 1

(MD007, ul-indent)


8-8: Unordered list indentation
Expected: 0; Actual: 1

(MD007, ul-indent)


9-9: Unordered list indentation
Expected: 0; Actual: 1

(MD007, ul-indent)


10-10: Unordered list indentation
Expected: 0; Actual: 1

(MD007, ul-indent)

🪛 Ruff (0.14.6)
tests/test_field_tracking_coverage.py

372-372: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


394-394: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


416-416: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


431-431: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


447-447: Unpacked variable overwritten is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


447-447: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


461-461: Unpacked variable prev_run is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

tests/test_plot_expression_coverage.py

88-88: Do not assert blind exception: Exception

(B017)


132-132: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


214-214: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


217-219: try-except-pass detected, consider logging the exception

(S110)


217-217: Do not catch blind exception: Exception

(BLE001)


232-232: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


235-237: try-except-pass detected, consider logging the exception

(S110)


235-235: Do not catch blind exception: Exception

(BLE001)


250-250: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


253-254: try-except-pass detected, consider logging the exception

(S110)


253-253: Do not catch blind exception: Exception

(BLE001)


268-268: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


271-272: try-except-pass detected, consider logging the exception

(S110)


271-271: Do not catch blind exception: Exception

(BLE001)


285-285: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


288-289: try-except-pass detected, consider logging the exception

(S110)


288-288: Do not catch blind exception: Exception

(BLE001)


303-303: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


306-307: try-except-pass detected, consider logging the exception

(S110)


306-306: Do not catch blind exception: Exception

(BLE001)


324-326: try-except-pass detected, consider logging the exception

(S110)


324-324: Do not catch blind exception: Exception

(BLE001)


344-345: try-except-pass detected, consider logging the exception

(S110)


344-344: Do not catch blind exception: Exception

(BLE001)


359-360: try-except-pass detected, consider logging the exception

(S110)


359-359: Do not catch blind exception: Exception

(BLE001)


413-413: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


415-416: try-except-pass detected, consider logging the exception

(S110)


415-415: Do not catch blind exception: Exception

(BLE001)


444-444: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


446-447: try-except-pass detected, consider logging the exception

(S110)


446-446: Do not catch blind exception: Exception

(BLE001)


496-496: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


498-499: try-except-pass detected, consider logging the exception

(S110)


498-498: Do not catch blind exception: Exception

(BLE001)


520-520: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


522-523: try-except-pass detected, consider logging the exception

(S110)


522-522: Do not catch blind exception: Exception

(BLE001)


584-584: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


586-587: try-except-pass detected, consider logging the exception

(S110)


586-586: Do not catch blind exception: Exception

(BLE001)


600-600: Unpacked variable axs is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


602-603: try-except-pass detected, consider logging the exception

(S110)


602-602: Do not catch blind exception: Exception

(BLE001)

kompot/cli/compute_config.py

23-23: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


108-108: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


128-128: f-string without any placeholders

Remove extraneous f prefix

(F541)


169-169: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


222-222: Do not catch blind exception: Exception

(BLE001)

tests/test_runinfo_coverage.py

123-123: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


295-295: Avoid equality comparisons to True; use summary['has_groups']: for truth checks

Replace with summary['has_groups']

(E712)


452-452: Loop control variable key not used within loop body

Rename unused key to _key

(B007)


490-490: Loop control variable location not used within loop body

Rename unused location to _location

(B007)

tests/test_heatmap_visualization_coverage.py

23-23: Unpacked variable cmap_obj is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


36-36: Unpacked variable cmap_obj is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


47-47: Unpacked variable norm is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


47-47: Unpacked variable cmap_obj is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


57-57: Unpacked variable norm is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


57-57: Unpacked variable cmap_obj is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


68-68: Unpacked variable norm is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


68-68: Unpacked variable vmin is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


68-68: Unpacked variable vmax is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


74-74: Using hasattr(x, "__call__") to test if x is callable is unreliable. Use callable(x) for consistent results.

Replace with callable()

(B004)


80-80: Unpacked variable norm is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


80-80: Unpacked variable vmin is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


80-80: Unpacked variable vmax is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


89-89: Unpacked variable norm is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


89-89: Unpacked variable cmap_obj is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

tests/test_cli_compute_config.py

55-55: Unused method argument: monkeypatch

(ARG002)


68-70: try-except-pass detected, consider logging the exception

(S110)


68-68: Do not catch blind exception: Exception

(BLE001)


85-85: Unused method argument: monkeypatch

(ARG002)


167-167: Unused method argument: monkeypatch

(ARG002)


198-198: Unused method argument: capsys

(ARG002)


424-424: Unused function argument: adata

(ARG001)


424-424: Unused function argument: kwargs

(ARG001)


484-484: Unused function argument: adata

(ARG001)


484-484: Unused function argument: kwargs

(ARG001)


592-592: Unused function argument: adata

(ARG001)


592-592: Unused function argument: kwargs

(ARG001)


841-841: Unused function argument: adata

(ARG001)


841-841: Unused function argument: kwargs

(ARG001)


899-899: Unused function argument: adata

(ARG001)


899-899: Unused function argument: kwargs

(ARG001)


1003-1003: Unused function argument: adata

(ARG001)


1003-1003: Unused function argument: kwargs

(ARG001)

tests/test_cli_dm_coverage.py

164-164: Avoid specifying long messages outside the exception class

(TRY003)


293-293: Unused function argument: kwargs

(ARG001)


298-298: Local variable original_palantir is assigned to but never used

Remove assignment to unused variable original_palantir

(F841)


329-329: Local variable args is assigned to but never used

Remove assignment to unused variable args

(F841)

kompot/cli/da.py

264-264: Do not catch blind exception: Exception

(BLE001)

kompot/cli/de.py

281-281: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test (3.9)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.9)
🔇 Additional comments (61)
.github/workflows/tests.yml (1)

5-5: LGTM!

The addition of the dev branch to both push and pull_request triggers is appropriate and aligns with the release workflow (dev→main merge). The workflow configuration is sound: pinned action versions, correct JAX CPU setup to avoid CI GPU dependencies, and proper pytest/coverage integration.

Also applies to: 7-7

tests/test_cleanup_multiple_runs.py (1)

8-8: Reduced default n_cells to 60 looks good

Lowering the default from 100 to 60 keeps the data shape and test logic intact (no assertions depend on absolute cell counts) while reducing test runtime and memory use. This is consistent with the broader goal of shrinking test datasets across the suite.

tests/test_fdr_integration.py (1)

10-10: Test optimization looks reasonable with appropriate compensations.

The reduction from 100 to 60 cells (30 per condition) decreases test runtime while maintaining test validity through stronger effect sizes (5-10x fold changes on line 31). For integration tests, this trade-off is acceptable.

However, note that real-world edge cases with smaller effect sizes or borderline statistical significance may not be well-represented in these tests.

tests/test_direction_plot.py (1)

17-17: LGTM! Test data size optimization.

Reducing the default test data size from 100 to 60 cells improves test performance while maintaining adequate coverage.

tests/test_plot_functions.py (1)

18-18: LGTM! Test data size optimization.

Consistent with the test suite optimization to reduce default data sizes for faster test execution.

kompot/version.py (1)

3-3: LGTM! Version bump for release.

Version correctly updated to 0.6.2, consistent with the PR objectives and CHANGELOG.

tests/test_ptp_functionality.py (1)

15-15: LGTM! Test data size optimization.

Reducing default cells from 100 to 60 improves test performance, consistent with the broader test suite optimization.

tests/test_posterior_covariance.py (1)

12-12: LGTM! Test data size optimization.

Reducing default cells from 100 to 60 aligns with the test suite-wide performance optimization.

tests/test_fdr_edge_cases.py (1)

9-9: LGTM! Test data size optimization.

Default test data size reduced from 100 to 60 cells, consistent with the performance improvements across the test suite.

tests/test_volcano_multi_da.py (1)

17-17: LGTM! Test data size optimization.

Default test cells reduced from 100 to 60, improving test performance while maintaining coverage.

docs/source/cli.rst (1)

248-255: Compute options docs align with new CLI behavior

The new “Compute Options” sections for --use-gpu and --threads under both DE and DA commands are clear and match the compute-configuration features added in this release. No issues from a docs/UX perspective.

Also applies to: 336-343

kompot/plot/expression.py (1)

223-230: Robust None-handling in inferred layer selection

Adding the inferred_layer is not None guard before checking for "fold_change" avoids a potential TypeError when the stored layer is None, while still excluding fold-change layers from expression visualization and logging the fallback correctly. This is a safe and behavior-preserving fix.

tests/test_cleanup.py (1)

8-40: Reduced test fixture size is safe and improves test performance

Changing create_test_adata_for_cleanup to use 60 cells by default keeps all test logic intact (splits and landmarks are still well within bounds) while reducing computation and memory in the cleanup tests.

tests/test_anndata_representation_check.py (1)

12-67: Underrepresentation test fixture shrink preserves intended imbalance

Updating the helper to n_cells=60 and adjusting its usages keeps the extreme imbalance between conditions within each tissue (e.g. ~95/5 splits per tissue) while reducing test cost. The expectations in the tests (which assert specific tissues/conditions are underrepresented) remain consistent with the new counts.

Also applies to: 71-71, 170-173, 225-229, 276-280, 333-336, 401-405, 440-446

tests/test_volcano_de.py (1)

17-58: Smaller volcano test AnnData fixture keeps behavior but speeds tests

Reducing the default n_cells to 60 while still splitting evenly between groups A and B maintains the structure required by the volcano plot tests and improves test runtime/memory.

tests/test_de_error_handling.py (1)

10-25: DE error-handling tests remain valid with smaller default n_cells

The updated create_test_adata default of 60 cells still yields balanced A/B conditions and consistent obsm shapes, while specific tests that exercise edge sizes override n_cells explicitly. Error-handling coverage is preserved with lower resource usage.

tests/test_single_condition_variance.py (2)

19-37: Sample layouts correctly exercise single-/multi-sample variance scenarios

The updated 60-cell setup and revised samples_* assignments still match the intended scenarios:

  • base sample column: cond1 has two samples, cond2 one sample;
  • sample_both: both conditions have multiple samples;
  • sample_fallback / sample_reverse: only one condition has multiple samples, triggering the fallback reuse of a single-condition variance;
  • sample_both_fail: both conditions single-sample, ensuring the expected error path.

This keeps the variance-handling edge cases well covered with a smaller fixture.

Also applies to: 29-31, 105-110, 163-169, 188-190, 209-213


154-161: Relaxed tolerance for comparing DE results without sample_col is reasonable

Relaxing rtol to 0.05 when comparing mahalanobis values between allow_single_condition_variance=False and True (with sample_col=None) is appropriate given the reduced dataset size and any minor numerical variability, while still asserting that both modes produce effectively equivalent results in the no-sample-col case.

tests/test_de_comprehensive_params.py (1)

12-16: Test data size optimization looks appropriate.

The reduction from 120 to 50 cells with n_landmarks=10 is a reasonable trade-off for faster test execution while still maintaining adequate coverage for parameter combination testing.

kompot/anndata/differential_expression.py (5)

2368-2376: Group-wise FDR varm key initialization looks correct.

The conditional addition of FDR-related varm keys (local_fdr_groups, is_de_groups, ptp_groups) is properly gated by use_fdr, null_gene_indices, and compute_mahalanobis flags, consistent with the global FDR logic.


2494-2510: Correct handling of null genes in subset mean_log_fold_change.

The logic properly detects when subset results include null genes (by checking against expanded_genes length) and extracts only the real genes. The fallback warning for unexpected lengths is appropriate.


2544-2560: Consistent null gene handling for mahalanobis_distances.

Same correct pattern as mean_log_fold_change - extracts real genes when the array includes null genes from FDR computation.


2574-2614: Group-wise FDR computation implementation looks solid.

The implementation correctly:

  1. Guards computation with appropriate conditions (use_fdr, null_gene_indices, compute_mahalanobis)
  2. Validates array length before splitting real/null genes
  3. Reuses compute_fdr_statistics for consistency with global FDR
  4. Logs informative per-group DE summaries

2655-2685: Field mapping for group-wise FDR is comprehensive.

The field mappings correctly document all new group-wise varm keys with appropriate metadata including location, type, description, and the list of subsets contained in each matrix.

tests/test_groupwise_fdr_integration.py (3)

18-48: Well-structured test fixture with appropriate data setup.

The fixture creates test data with:

  • Clear differential expression signal (first 10 genes shifted by +3.0 in condition2)
  • Three balanced groups across two conditions
  • Appropriate sizes (120 cells, 80 genes) for meaningful FDR testing

119-143: Good validation of boolean dtype for is_de.

Testing that is_de values maintain boolean dtype is important for downstream filtering operations. This catches potential issues where the values might become floats or objects.


230-255: Excellent regression test for length mismatch warnings.

Capturing log output to verify no length-mismatch warnings are emitted is a valuable regression test that will catch issues if the null gene handling logic breaks in the future.

tests/test_de_advanced_features.py (4)

21-39: Good test data setup with appropriate sample structure.

The setup creates balanced conditions with multiple samples per condition, which is necessary for testing sample variance features.


41-73: Thorough FDR edge case testing.

The test correctly creates extreme expression differences to trigger zero p-values, then validates that the FDR computation handles this gracefully with valid probability outputs.


154-196: Good pattern for handling optional feature compatibility.

The try/except with pytest.skip for the progress parameter is a clean way to handle version differences in the underlying mellon library without failing the test suite.


245-268: Perfect! I have completed the verification. Let me now generate the final rewritten review comment.


Dict-based groups format is correct and matches parse_groups implementation.

The groups_dict structure with inner dicts using {'cell_type': 'TypeX'} format is the correct "named filters" pattern. The parse_groups function explicitly handles dict-of-dicts input where outer keys are filter names and inner dicts specify column conditions. The test setup properly creates a cell_type column in adata.obs with matching values (TypeX, TypeY), so the format will work as intended.

tests/test_volcano_de_edge_cases.py (1)

17-17: Test data size optimization consistent with other test files.

The reduction to 60 cells aligns with similar optimizations in other volcano test files (test_volcano_de.py, test_volcano_de_fdr.py, etc.) as noted in the AI summary.

tests/test_store_additional_stats.py (1)

8-8: LGTM - consistent with test suite optimization.

The reduction of default n_cells from 100 to 60 aligns with the broader PR pattern of reducing test data sizes across the test suite for faster execution while maintaining test coverage.

tests/test_html_representation.py (1)

11-11: LGTM - consistent test data sizing.

The default n_cells=60 matches the pattern across other test modules (test_plot_functions.py, test_direction_plot.py, test_volcano_multi_da.py, etc.) for streamlined test execution.

tests/test_anndata_differential_abundance.py (1)

15-15: LGTM - cleaner sample division.

The change to n_cells=60 actually improves the with_samples=True case: 60 cells divide evenly by 6 (3 samples × 2 conditions), giving exactly 10 cells per sample without remainders.

kompot/cli/da.py (2)

151-162: LGTM - CLI compute configuration options.

The --use-gpu and --threads arguments are well-documented and follow the existing CLI argument patterns. The help text clearly explains their purpose.


196-209: LGTM - early logging of compute configuration.

Logging the GPU and thread configuration before setup provides good visibility into the intended compute environment.

tests/test_anndata_groups.py (2)

33-35: LGTM - necessary filtering for new FDR keys.

The addition of "fdr" not in key correctly prevents false matches with the new group-wise FDR statistics keys (e.g., mahalanobis_local_fdr). The comment on line 34 clearly explains the intent.


44-44: LGTM - consistent test data sizing.

Aligns with the PR-wide pattern of reducing default test cell counts to 60.

kompot/cli/main.py (2)

12-48: LGTM - early thread limit configuration.

The pre-argparse parsing of --threads is a clever solution for setting environment variables before NumPy initialization. The function correctly handles both --threads VALUE and --threads=VALUE formats, and appropriately defers invalid value handling to the full argparse parsing.


53-59: LGTM - correct import deferral pattern.

Moving subcommand imports after _set_early_thread_limits() ensures NumPy (imported by those modules) respects the thread limits.

tests/test_runinfo_coverage.py (2)

16-46: Well-structured test fixture with realistic data.

The fixture properly sets up AnnData with differential expression history, using appropriate random seeds for reproducibility and reasonable test data sizes.


566-603: Good edge case coverage for RunInfo and RunComparison.

The edge case tests effectively cover scenarios like JSON string field mappings, missing field mappings, and comparing a run with itself. This helps ensure robustness.

tests/test_volcano_de_fdr.py (1)

14-16: Reasonable test data size reduction.

Reducing n_cells from 100 to 60 aligns with the PR objective to optimize test data sizes for improved test runtime while maintaining adequate coverage.

kompot/cli/de.py (1)

164-175: New compute configuration CLI options look good.

The --use-gpu and --threads options are well-documented with appropriate help text and correctly use action='store_true' for the boolean flag and type=int for threads.

tests/test_field_tracking_coverage.py (2)

168-302: Comprehensive field name generation tests.

Excellent coverage of generate_output_field_names including DA/DE types, sample suffix handling, sanitization, and error cases. This will help catch regressions in field naming logic.


616-741: Thorough edge case testing for get_run_from_history.

Good coverage of empty history, None run_id, positive/negative indices, out-of-range indices, JSON string handling, invalid JSON, non-dict items, custom history keys, and immutability verification.

kompot/cli/compute_config.py (4)

219-223: Broad exception catch is acceptable here for robustness.

Catching Exception when querying device platform is reasonable since this is informational and shouldn't fail the program. The fallback to 'unknown' is appropriate.


78-106: Thread limit configuration is well-structured.

Setting OMP, MKL, OpenBLAS, and BLAS thread limits covers the major NumPy backends. Good logging at both info and debug levels.


195-237: Clean implementation of get_device_info().

Handles missing JAX gracefully, checks for GPU availability, and returns a well-structured dictionary. Good defensive coding.


148-166: XLA_FLAGS formatting is correct per official documentation.

The current implementation properly follows the documented XLA_FLAGS format. Official JAX documentation specifies that XLA_FLAGS should be a space-separated string with -- prefixed flags and non-prefixed parameters (e.g., --xla_cpu_multi_thread_eigen=false intra_op_parallelism_threads=1). The code correctly combines both flags in a single string without requiring separate formatting or additional -- prefixes. No changes needed.

tests/test_cli_dm_coverage.py (2)

18-35: Consider using shared fixtures from conftest.py.

This fixture duplicates functionality already provided by small_adata in tests/conftest.py. Using shared fixtures would reduce code duplication and ensure consistent test data across the test suite.

However, this fixture includes PCA data (X_pca) which is specifically required for DM tests, so keeping it as a specialized fixture is reasonable.


368-425: Good test structure with proper mocking.

The success path tests properly mock palantir.utils.run_diffusion_maps and verify output file creation and content. The graceful handling of missing palantir dependency via pytest.skip is appropriate.

tests/test_cli_compute_config.py (2)

371-382: Mock function signatures are appropriate.

The unused adata and kwargs parameters in mock functions (e.g., mock_compute_de) are necessary to match the signature of the real compute_differential_expression function. The static analysis warnings for ARG001 are false positives in this context.


1-16: Good test module structure.

Comprehensive coverage for CLI compute configuration including thread limits, JAX/Dask configuration, device info, and DE/DA CLI flows. The test organization is clear and follows pytest conventions.

tests/conftest.py (3)

9-11: Good practice: JAX environment configured before imports.

Setting JAX_PLATFORMS and JAX_ENABLE_X64 before JAX is imported ensures consistent CPU-only testing behavior. This is the correct approach for test environments.


31-68: Well-designed tiered fixture system.

The fixtures provide appropriately sized test data for different test scenarios:

  • tiny_adata: Fast unit tests (~0.01s)
  • small_adata: Standard unit tests (~0.05s)
  • medium_adata: Integration tests (~0.2-0.5s)

This allows tests to choose the appropriate fixture based on their needs, balancing test coverage with execution speed.


181-212: Useful parameter fixtures for fast testing.

The fast_de_params and fast_da_params fixtures provide sensible defaults for quick test execution (small n_landmarks, disabled FDR, no progress bars). This is good for CI/CD pipelines where test speed matters.

tests/test_heatmap_visualization_coverage.py (3)

101-107: Good resource management with setup/teardown.

Properly creating figures in setup_method and closing them in teardown_method prevents matplotlib figure accumulation and memory leaks during test runs.


197-216: Comprehensive error handling tests.

Good coverage testing invalid axes input and various edge cases (NaN values, string conversion, colormap objects). The tests verify both successful paths and error conditions.


402-413: Good edge case: counts exceeding global_max_count.

Testing the clipping behavior when cell counts exceed global_max_count ensures the visualization function handles boundary conditions correctly.

Comment on lines +85 to +94
def test_infer_keys_strict_mode(self):
"""Test that strict mode raises error when keys can't be inferred."""
# AnnData without DE results
with pytest.raises(Exception): # Should raise when keys can't be inferred
_infer_expression_keys(
self.adata,
lfc_key=None,
score_key=None,
strict=True
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the _infer_expression_keys function implementation
find . -type f -name "*.py" | xargs grep -l "_infer_expression_keys" | head -20

Repository: settylab/kompot

Length of output: 190


🏁 Script executed:

#!/bin/bash
# Find and examine the _infer_expression_keys function implementation
rg -n "_infer_expression_keys" kompot/plot/expression.py -A 30 -B 2

Repository: settylab/kompot

Length of output: 2953


🏁 Script executed:

#!/bin/bash
# Get the full _infer_expression_keys function - read more lines
sed -n '18,100p' kompot/plot/expression.py

Repository: settylab/kompot

Length of output: 2760


🏁 Script executed:

#!/bin/bash
# Find the field_inference module and the infer_fields_from_run_info function
find . -type f -name "field_inference.py"

Repository: settylab/kompot

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Find the infer_fields_from_run_info function and examine its error handling
rg -n "def infer_fields_from_run_info" ./kompot/plot/field_inference.py -A 50

Repository: settylab/kompot

Length of output: 1943


🏁 Script executed:

#!/bin/bash
# Look for raise statements in the infer_fields_from_run_info function to confirm the exception type
rg -n "raise" ./kompot/plot/field_inference.py -B 2 -A 1

Repository: settylab/kompot

Length of output: 687


🏁 Script executed:

#!/bin/bash
# Examine the test to see what it's currently testing
sed -n '85,94p' tests/test_plot_expression_coverage.py

Repository: settylab/kompot

Length of output: 467


Use ValueError instead of broad Exception for strict mode error handling.

The _infer_expression_keys function calls infer_fields_from_run_info with strict=True, which raises ValueError (not KeyError) when required fields cannot be inferred. Using pytest.raises(Exception) is too broad and may mask unintended errors.

     def test_infer_keys_strict_mode(self):
         """Test that strict mode raises error when keys can't be inferred."""
         # AnnData without DE results
-        with pytest.raises(Exception):  # Should raise when keys can't be inferred
+        with pytest.raises(ValueError):  # Should raise when keys can't be inferred
             _infer_expression_keys(
                 self.adata,
                 lfc_key=None,
                 score_key=None,
                 strict=True
             )
📝 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.

Suggested change
def test_infer_keys_strict_mode(self):
"""Test that strict mode raises error when keys can't be inferred."""
# AnnData without DE results
with pytest.raises(Exception): # Should raise when keys can't be inferred
_infer_expression_keys(
self.adata,
lfc_key=None,
score_key=None,
strict=True
)
def test_infer_keys_strict_mode(self):
"""Test that strict mode raises error when keys can't be inferred."""
# AnnData without DE results
with pytest.raises(ValueError): # Should raise when keys can't be inferred
_infer_expression_keys(
self.adata,
lfc_key=None,
score_key=None,
strict=True
)
🧰 Tools
🪛 Ruff (0.14.6)

88-88: Do not assert blind exception: Exception

(B017)

🤖 Prompt for AI Agents
In tests/test_plot_expression_coverage.py around lines 85 to 94, the test
currently expects a broad Exception when keys can't be inferred; replace
pytest.raises(Exception) with pytest.raises(ValueError) so the test specifically
asserts the ValueError raised by strict mode in
_infer_expression_keys/infer_fields_from_run_info, keeping the rest of the call
unchanged.

@katosh katosh merged commit 3ba5773 into main Nov 30, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants