diff --git a/genoray/_vcf.py b/genoray/_vcf.py index 77283a0..5df5b71 100644 --- a/genoray/_vcf.py +++ b/genoray/_vcf.py @@ -316,9 +316,16 @@ def _check_filter_pair( ) @property - def filter(self) -> Callable[[cyvcf2.Variant], bool] | None: - """Function to filter variants. Should return True for variants to keep.""" - return self._filter + def filter( + self, + ) -> tuple[Callable[[cyvcf2.Variant], bool] | None, pl.Expr | None]: + """The ``(filter, pl_filter)`` pair currently in effect. + + Returns the cyvcf2 record callable and its matching polars expression as + a tuple, mirroring what the setter accepts. Both elements are ``None`` + when no filter is set. Assigning ``vcf.filter = vcf.filter`` round-trips. + """ + return self._filter, self._pl_filter def _index_path(self) -> Path: """Path to the index file.""" diff --git a/skills/genoray-api/SKILL.md b/skills/genoray-api/SKILL.md index afacb90..b122e0c 100644 --- a/skills/genoray-api/SKILL.md +++ b/skills/genoray-api/SKILL.md @@ -187,11 +187,13 @@ predicates (e.g. `is_symbolic`), also pass the matching polars `pl.Expr` to To change a VCF's filter after construction, assign a `(filter, pl_filter)` tuple to the `vcf.filter` setter (or `None` to clear both); the same both-or-neither invariant is enforced, and the in-memory index is invalidated. -The getter still returns just the callable. +The getter returns the `(filter, pl_filter)` tuple, mirroring the setter +(`(None, None)` when unset), so `vcf.filter = vcf.filter` round-trips. ```python vcf.filter = (lambda rec: ..., ~genoray.exprs.is_symbolic) # set both vcf.filter = None # clear both +fn, expr = vcf.filter # get both ``` PGEN: pass a polars `pl.Expr` returning a boolean mask, operating on the diff --git a/tests/test_vcf.py b/tests/test_vcf.py index e83357c..2940e15 100644 --- a/tests/test_vcf.py +++ b/tests/test_vcf.py @@ -322,13 +322,17 @@ def record_fn(v): # Setting a valid (filter, pl_filter) pair updates both and invalidates the index. vcf._index = "sentinel" vcf.filter = (record_fn, pl_expr) - assert vcf.filter is record_fn + assert vcf.filter == (record_fn, pl_expr) assert vcf._pl_filter is pl_expr assert vcf._index is None - # Assigning None clears both. + # The getter mirrors the setter, so assigning the getter back round-trips. + vcf.filter = vcf.filter + assert vcf.filter == (record_fn, pl_expr) + + # Assigning None clears both; the getter returns (None, None). vcf.filter = None - assert vcf.filter is None + assert vcf.filter == (None, None) assert vcf._pl_filter is None # A mismatched pair raises ValueError and leaves state untouched. @@ -336,7 +340,7 @@ def record_fn(v): vcf.filter = (record_fn, None) with pytest.raises(ValueError): vcf.filter = (None, pl_expr) - assert vcf.filter is None + assert vcf.filter == (None, None) assert vcf._pl_filter is None # A bare (non-tuple) value is rejected.