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
7 changes: 7 additions & 0 deletions .github/workflows/code-checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Setup uv
uses: astral-sh/setup-uv@v2
- name: Check validate.pyi is up to date
run: |
uv run make pyi
git diff --exit-code pointblank/validate.pyi || \
(echo "validate.pyi is out of date — run 'make pyi' and commit the result" && exit 1)
- uses: pre-commit/action@v3.0.1
13 changes: 10 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
exclude: "(.*\\.svg)|(.*\\.qmd)|(.*\\.ambr)|(.*\\.csv)|(.*\\.txt)|(.*\\.json)|(.*\\.ipynb)|(.*\\.html)"
repos:
- repo: local
hooks:
- id: check-pyi-sync
name: validate.pyi must be up to date
language: system
entry: bash -c 'make pyi && git diff --exit-code pointblank/validate.pyi'
pass_filenames: false
stages: [commit]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
# NOTE: ruff version must match the pin in pyproject.toml [dependency-groups] dev
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9
rev: v0.14.10
hooks:
# Run the linter.
- id: ruff
args: [--fix]
# Run the formatter.
- id: ruff-format
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pyi: ## Generate .pyi stub files
--include-private \
-o .
@uv run scripts/generate_agg_validate_pyi.py
@uv run ruff check --fix pointblank/validate.pyi
@uv run ruff format pointblank/validate.pyi

.PHONY: test
test:
Expand Down
17 changes: 15 additions & 2 deletions pointblank/_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ def _generic_between(real: float, lower: float, upper: float) -> bool:
return bool(lower <= real <= upper)


def split_agg_name(name: str) -> tuple[str, str]:
"""Split an aggregation method name into aggregator and comparator names.

Args:
name (str): The aggregation method name (e.g., "col_sum_eq" or "sum_eq").

Returns:
tuple[str, str]: A tuple of (agg_name, comp_name) e.g., ("sum", "eq").
"""
name = name.removeprefix("col_")
agg_name, comp_name = name.rsplit("_", 1)
return agg_name, comp_name


def resolve_agg_registries(name: str) -> tuple[Aggregator, Comparator]:
"""Resolve the assertion name to a valid aggregator

Expand All @@ -95,8 +109,7 @@ def resolve_agg_registries(name: str) -> tuple[Aggregator, Comparator]:
Returns:
tuple[Aggregator, Comparator]: The aggregator and comparator functions.
"""
name = name.removeprefix("col_")
agg_name, comp_name = name.split("_")[-2:]
agg_name, comp_name = split_agg_name(name)

aggregator = AGGREGATOR_REGISTRY.get(agg_name)
comparator = COMPARATOR_REGISTRY.get(comp_name)
Expand Down
62 changes: 61 additions & 1 deletion pointblank/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
from great_tables.vals import fmt_integer, fmt_number
from importlib_resources import files

from pointblank._agg import is_valid_agg, load_validation_method_grid, resolve_agg_registries
from pointblank._agg import (
is_valid_agg,
load_validation_method_grid,
resolve_agg_registries,
split_agg_name,
)
from pointblank._constants import (
ASSERTION_TYPE_METHOD_MAP,
CHECK_MARK_SPAN,
Expand Down Expand Up @@ -18878,6 +18883,15 @@ def _create_autobrief_or_failure_text(
for_failure=for_failure,
)

if is_valid_agg(assertion_type):
return _create_text_agg(
lang=lang,
assertion_type=assertion_type,
column=column,
values=values,
for_failure=for_failure,
)

return None


Expand Down Expand Up @@ -18912,6 +18926,52 @@ def _create_text_comparison(
)


def _create_text_agg(
lang: str,
assertion_type: str,
column: str | list[str],
values: dict[str, Any],
for_failure: bool = False,
) -> str:
"""Create autobrief text for aggregation methods like col_sum_eq, col_avg_gt, etc."""
type_ = _expect_failure_type(for_failure=for_failure)

agg_type, comp_type = split_agg_name(assertion_type)

# this is covered by the test `test_brief_auto_all_agg_methods` to make sure we don't
# create any weird secret agg constants.
agg_display_names: dict[str, str] = {
"sum": "sum",
"avg": "average",
"sd": "standard deviation",
}
try:
agg_display: str = agg_display_names[agg_type]
except KeyError as ke: # pragma: no cover
raise AssertionError from ke # This should never happen in prod, it's caught in CI.

# Get the operator
comparison_assertion = f"col_vals_{comp_type}"
if lang == "ar": # pragma: no cover
operator = COMPARISON_OPERATORS_AR.get(comparison_assertion, comp_type)
else:
operator = COMPARISON_OPERATORS.get(comparison_assertion, comp_type)

column_text = _prep_column_text(column=column)

value = values.get("value", values) if isinstance(values, dict) else values
values_text = _prep_values_text(values=str(value), lang=lang, limit=3)

# "Expect that the {agg} of {column} should be {operator} {value}."
agg_expectation_text = EXPECT_FAIL_TEXT[f"compare_{type_}_text"][lang]

return agg_expectation_text.format(
column_text=f"the {agg_display} of {column_text}",
operator=operator,
values_text=values_text,
)


def _create_text_between(
lang: str,
column: str,
Expand Down
35 changes: 15 additions & 20 deletions pointblank/validate.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
from pointblank import Actions, Thresholds
from pointblank._utils import _PBUnresolvedColumn
from pointblank.column import Column, ReferenceColumn
from pointblank._typing import Tolerance

import datetime
from collections.abc import Collection
from dataclasses import dataclass
Expand Down Expand Up @@ -586,7 +581,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sum to a value eq some `value`.

Expand Down Expand Up @@ -622,7 +617,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sum to a value gt some `value`.

Expand Down Expand Up @@ -658,7 +653,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sum to a value ge some `value`.

Expand Down Expand Up @@ -694,7 +689,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sum to a value lt some `value`.

Expand Down Expand Up @@ -730,7 +725,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sum to a value le some `value`.

Expand Down Expand Up @@ -766,7 +761,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column avg to a value eq some `value`.

Expand Down Expand Up @@ -802,7 +797,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column avg to a value gt some `value`.

Expand Down Expand Up @@ -838,7 +833,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column avg to a value ge some `value`.

Expand Down Expand Up @@ -874,7 +869,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column avg to a value lt some `value`.

Expand Down Expand Up @@ -910,7 +905,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column avg to a value le some `value`.

Expand Down Expand Up @@ -946,7 +941,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sd to a value eq some `value`.

Expand Down Expand Up @@ -982,7 +977,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sd to a value gt some `value`.

Expand Down Expand Up @@ -1018,7 +1013,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sd to a value ge some `value`.

Expand Down Expand Up @@ -1054,7 +1049,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sd to a value lt some `value`.

Expand Down Expand Up @@ -1090,7 +1085,7 @@ class Validate:
thresholds: float | bool | tuple | dict | Thresholds | None = None,
brief: str | bool = False,
actions: Actions | None = None,
active: bool = True,
active: bool | Callable = True,
) -> Validate:
"""Assert the values in a column sd to a value le some `value`.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ dev = [
"pytest-xdist>=3.6.1",
"pytz>=2025.2",
"quartodoc>=0.8.1; python_version >= '3.9'",
"ruff>=0.9.9",
"ruff==0.14.10", # NOTE: must match rev in .pre-commit-config.yaml
"shiny>=1.4.0",
"openpyxl>=3.0.0",
"mcp[cli]>=1.10.1",
Expand Down
Loading
Loading