Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions genoray/_vcf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
4 changes: 3 additions & 1 deletion skills/genoray-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions tests/test_vcf.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,21 +322,25 @@ 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.
with pytest.raises(ValueError):
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.
Expand Down
Loading