Skip to content

Commit 76e7c27

Browse files
committed
fix: migrate _check_types from typeguard to pydantic validate_call
1 parent d1ace55 commit 76e7c27

1 file changed

Lines changed: 39 additions & 34 deletions

File tree

malariagen_data/util.py

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,48 +1157,53 @@ def _check_colab_location(gcp_region: Optional[str]):
11571157
def _check_types(f):
11581158
"""Decorator to provide runtime checking of parameter types.
11591159
1160-
Uses Pydantic v2's TypeAdapter for parameter validation.
1161-
Validates input types without coercing arguments — the original
1162-
function is called with the original unmodified arguments.
1160+
Uses Pydantic v2's validate_call() for parameter validation.
1161+
Validates input types in strict mode without coercing arguments.
11631162
11641163
"""
1165-
from pydantic import ConfigDict, TypeAdapter, ValidationError
1164+
from pydantic import ConfigDict, ValidationError, validate_call
11661165

1167-
type_hints = get_type_hints(f)
1168-
config = ConfigDict(arbitrary_types_allowed=True)
1166+
config = ConfigDict(strict=True, arbitrary_types_allowed=True)
11691167

1170-
# Build a TypeAdapter for each annotated parameter (skip 'return').
1171-
adapters: dict = {}
1172-
for k, t in type_hints.items():
1173-
if k == "return":
1174-
continue
1175-
try:
1176-
adapters[k] = TypeAdapter(t, config=config)
1177-
except Exception:
1178-
pass # Skip types pydantic cannot handle
1168+
try:
1169+
validated_f = validate_call(config=config, validate_return=False)(f)
1170+
except Exception as exc:
1171+
warnings.warn(
1172+
f"Could not apply validate_call to {f.__name__!r}: {exc}. "
1173+
"Type validation will be skipped for this function.",
1174+
stacklevel=2,
1175+
)
1176+
return f
11791177

11801178
@wraps(f)
11811179
def wrapper(*args, **kwargs):
1182-
call_args = getcallargs(f, *args, **kwargs)
1183-
for k, adapter in adapters.items():
1184-
if k in call_args:
1185-
v = call_args[k]
1186-
try:
1187-
adapter.validate_python(v, strict=True)
1188-
except ValidationError as e:
1189-
expected_type = humanize_type(type_hints[k])
1190-
actual_type = humanize_type(type(v))
1191-
message = fill(
1192-
dedent(
1193-
f"""
1194-
Parameter {k!r} with value {v!r} in call to function {f.__name__!r} has incorrect type:
1195-
found {actual_type}, expected {expected_type}. See below for further information.
1196-
"""
1180+
try:
1181+
return validated_f(*args, **kwargs)
1182+
except ValidationError as e:
1183+
type_hints = get_type_hints(f)
1184+
call_args = getcallargs(f, *args, **kwargs)
1185+
errors = e.errors()
1186+
if errors:
1187+
err = errors[0]
1188+
loc = err.get("loc", ())
1189+
if loc:
1190+
k = str(loc[0])
1191+
v = call_args.get(k)
1192+
t = type_hints.get(k)
1193+
if t is not None:
1194+
expected_type = humanize_type(t)
1195+
actual_type = humanize_type(type(v))
1196+
message = fill(
1197+
dedent(
1198+
f"""\
1199+
Parameter {k!r} with value {v!r} in call to function {f.__name__!r} has incorrect type:
1200+
found {actual_type}, expected {expected_type}. See below for further information.
1201+
"""
1202+
)
11971203
)
1198-
)
1199-
message += f"\n\n{e}"
1200-
raise TypeError(message) from None
1201-
return f(*args, **kwargs)
1204+
message += f"\n\n{e}"
1205+
raise TypeError(message) from None
1206+
raise TypeError(str(e)) from None
12021207

12031208
return wrapper
12041209

0 commit comments

Comments
 (0)