scatrans 0.9.8.dev5__tar.gz → 0.9.8.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/CHANGELOG.md +6 -0
  2. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/PKG-INFO +1 -1
  3. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/_version.py +2 -2
  4. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_regression_bugs.py +9 -0
  5. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/.github/workflows/ci.yml +0 -0
  6. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/.github/workflows/publish.yml +0 -0
  7. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/.gitignore +0 -0
  8. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/.pre-commit-config.yaml +0 -0
  9. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/CITATION.cff +0 -0
  10. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/LICENSE +0 -0
  11. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/MANIFEST.in +0 -0
  12. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/README.md +0 -0
  13. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/conftest.py +0 -0
  14. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/conftest_fixtures.py +0 -0
  15. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/examples/memento_de_example.py +0 -0
  16. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/examples/real_data_template.py +0 -0
  17. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/examples/synthetic_active_transcription.py +0 -0
  18. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/pyproject.toml +0 -0
  19. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/scatrans.egg-info/SOURCES.txt +0 -0
  20. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/setup.cfg +0 -0
  21. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/__init__.py +0 -0
  22. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/_de.py +12 -12
  23. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/_permutation.py +0 -0
  24. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/_utils.py +0 -0
  25. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/_velocity.py +0 -0
  26. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/DATA_LICENSES.md +0 -0
  27. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/Hs_GO_Biological_Process_2026.txt +0 -0
  28. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/Hs_KEGG_2026.txt +0 -0
  29. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/Mm_GO_Biological_Process_2026.txt +0 -0
  30. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/Mm_KEGG_2026.txt +0 -0
  31. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/Mus_musculus.GRCm39.115_gene_features.parquet +0 -0
  32. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/README.md +0 -0
  33. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/human_GRCh38_2024A_gene_features.parquet +0 -0
  34. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/data/mouse_2020A_gene_features.parquet +0 -0
  35. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/enrich.py +0 -0
  36. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/generate_gene_features.py +0 -0
  37. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/pl.py +0 -0
  38. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/pp_bias.py +0 -0
  39. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/qc.py +0 -0
  40. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/src/scatrans/tl.py +0 -0
  41. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/__init__.py +0 -0
  42. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/conftest.py +0 -0
  43. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_basic.py +0 -0
  44. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_de_backends.py +0 -0
  45. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_de_edge_cases.py +0 -0
  46. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_enrich_api.py +0 -0
  47. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_enrich_go.py +0 -0
  48. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_pl_coverage.py +0 -0
  49. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_pl_extended.py +0 -0
  50. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_pp_bias_cli.py +0 -0
  51. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_public_api.py +0 -0
  52. {scatrans-0.9.8.dev5 → scatrans-0.9.8.dev6}/tests/test_tl_coverage.py +0 -0
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [0.9.8 bugfix 2026-07-04]
9
9
 
10
10
  ### Fixed
11
+ - **CI pytest failure on matrix legs without `pydeseq2`**: two new pseudobulk regression tests
12
+ (`test_strict_pydeseq2_counts_rejects_log_normalized_*`) now use the same
13
+ `@pytest.mark.skipif(importlib.util.find_spec("pydeseq2") is None, ...)` guard as
14
+ `tests/test_de_backends.py`, instead of relying on production-code check ordering.
15
+ Restored PyDESeq2 `ImportError` (missing dependency) ahead of data-validation `ValueError`
16
+ in `_run_de_wrapper`, so users without `pydeseq2` see the install hint first.
11
17
  - **Pseudobulk `strict_pydeseq2_counts` check ran on already-rounded data**: `_pseudobulk_with_layers`
12
18
  always rounds aggregated sums to integers, so the PyDESeq2 count-likeness check in
13
19
  `_run_de_wrapper` (which inspected the rounded pseudobulk `.X`) could never detect that the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scatrans
3
- Version: 0.9.8.dev5
3
+ Version: 0.9.8.dev6
4
4
  Summary: Single-cell Active Transcription Analysis
5
5
  Author: scATrans Developers
6
6
  License: Apache-2.0
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.9.8.dev5'
22
- __version_tuple__ = version_tuple = (0, 9, 8, 'dev5')
21
+ __version__ = version = '0.9.8.dev6'
22
+ __version_tuple__ = version_tuple = (0, 9, 8, 'dev6')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import importlib.util
5
6
  import logging
6
7
  import sys
7
8
  from unittest.mock import patch
@@ -194,6 +195,10 @@ def test_diagnose_design_kb_python_layers(adata_mature_nascent):
194
195
  assert 0.0 <= diag["unspliced_global_fraction"] <= 1.0
195
196
 
196
197
 
198
+ @pytest.mark.skipif(
199
+ importlib.util.find_spec("pydeseq2") is None,
200
+ reason="pydeseq2 not installed",
201
+ )
197
202
  def test_strict_pydeseq2_counts_rejects_log_normalized_pseudobulk():
198
203
  """Rounding before integer check must not let log-normalized pb data through."""
199
204
  rng = np.random.default_rng(7)
@@ -238,6 +243,10 @@ def test_pseudobulk_with_layers_flags_non_count_source_before_rounding():
238
243
  assert pb.uns["pb_x_is_count_like"] is False
239
244
 
240
245
 
246
+ @pytest.mark.skipif(
247
+ importlib.util.find_spec("pydeseq2") is None,
248
+ reason="pydeseq2 not installed",
249
+ )
241
250
  def test_strict_pydeseq2_counts_rejects_log_normalized_layer_end_to_end():
242
251
  """End-to-end: use_pseudobulk=True + pb_x_layer pointing at log-normalized data must raise,
243
252
  not silently round the log-normalized sums into look-alike integers."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -185,6 +185,18 @@ def _run_de_wrapper(
185
185
  _ann_log.setLevel(_prev_ann)
186
186
 
187
187
  if is_pseudobulk and pb_backend == "pydeseq2":
188
+ try:
189
+ from pydeseq2.dds import DeseqDataSet
190
+ from pydeseq2.ds import DeseqStats
191
+ except ImportError as e:
192
+ raise ImportError(
193
+ "PyDESeq2 backend requested but 'pydeseq2' is not installed.\n"
194
+ "Install with:\n"
195
+ ' pip install "scatrans[pseudobulk]"\n'
196
+ "or\n"
197
+ " pip install pydeseq2"
198
+ ) from e
199
+
188
200
  n_t = (ad_temp.obs[use_groupby] == target_group).sum()
189
201
  n_r = (ad_temp.obs[use_groupby] == reference_group).sum()
190
202
  if n_t < 2 or n_r < 2:
@@ -256,18 +268,6 @@ def _run_de_wrapper(
256
268
  f"No genes passed the DESeq2 count filter (sum(counts) >= {min_counts_per_gene})."
257
269
  )
258
270
 
259
- try:
260
- from pydeseq2.dds import DeseqDataSet
261
- from pydeseq2.ds import DeseqStats
262
- except ImportError as e:
263
- raise ImportError(
264
- "PyDESeq2 backend requested but 'pydeseq2' is not installed.\n"
265
- "Install with:\n"
266
- ' pip install "scatrans[pseudobulk]"\n'
267
- "or\n"
268
- " pip install pydeseq2"
269
- ) from e
270
-
271
271
  condition = ad_temp.obs[use_groupby].astype(str).values
272
272
  metadata = pd.DataFrame(
273
273
  {use_groupby: pd.Categorical(condition, categories=[reference_group, target_group])},