|
19 | 19 | from typing import Any |
20 | 20 |
|
21 | 21 | from . import _apply_generic |
22 | | -from ._special_form import _special_form_evaluator |
| 22 | +from ._special_form import ( |
| 23 | + BoolSpecialMetadata, |
| 24 | + _bool_special_form_registry, |
| 25 | + _special_form_evaluator, |
| 26 | +) |
23 | 27 |
|
24 | 28 |
|
25 | 29 | __all__ = ("eval_typing",) |
@@ -346,13 +350,56 @@ def _eval_applied_type_alias(obj: types.GenericAlias, ctx: EvalContext): |
346 | 350 | return evaled |
347 | 351 |
|
348 | 352 |
|
| 353 | +def _eval_bool_special_form( |
| 354 | + metadata: BoolSpecialMetadata, |
| 355 | + new_args: tuple[typing.Any, ...], |
| 356 | + ctx: EvalContext, |
| 357 | +) -> bool: |
| 358 | + import ast |
| 359 | + |
| 360 | + original_cls = metadata.cls |
| 361 | + |
| 362 | + try: |
| 363 | + namespace = {} |
| 364 | + |
| 365 | + # Add the class's module |
| 366 | + if cls_module := sys.modules.get(original_cls.__module__): |
| 367 | + namespace.update(cls_module.__dict__) |
| 368 | + |
| 369 | + # Add type parameters with their substituted values |
| 370 | + type_params = metadata.type_params |
| 371 | + if type_params and new_args: |
| 372 | + for param, arg in zip(type_params, new_args, strict=False): |
| 373 | + namespace[param.__name__] = arg |
| 374 | + |
| 375 | + expr = compile( |
| 376 | + ast.Expression(body=metadata.expr_node), # type: ignore[arg-type] |
| 377 | + '<bool_expr>', |
| 378 | + 'eval', |
| 379 | + ) |
| 380 | + bool_expr = eval(expr, namespace) |
| 381 | + |
| 382 | + # Evaluate the type expression |
| 383 | + result = _eval_types(bool_expr, ctx) |
| 384 | + |
| 385 | + return result |
| 386 | + |
| 387 | + except Exception as e: |
| 388 | + raise RuntimeError( |
| 389 | + f"Failed to evaluate special form for {original_cls.__name__}: {e}" |
| 390 | + ) from e |
| 391 | + |
| 392 | + |
349 | 393 | @_eval_types_impl.register |
350 | 394 | def _eval_applied_class(obj: typing_GenericAlias, ctx: EvalContext): |
351 | 395 | """Eval a typing._GenericAlias -- an applied user-defined class""" |
352 | 396 | # generic *classes* are typing._GenericAlias while generic type |
353 | 397 | # aliases are types.GenericAlias? Why in the world. |
354 | 398 | new_args = tuple(_eval_types(arg, ctx) for arg in typing.get_args(obj)) |
355 | 399 |
|
| 400 | + if metadata := _bool_special_form_registry.get(obj.__origin__): |
| 401 | + return _eval_bool_special_form(metadata, new_args, ctx) |
| 402 | + |
356 | 403 | if func := _eval_funcs.get(obj.__origin__): |
357 | 404 | ret = func(*new_args, ctx=ctx) |
358 | 405 | # return _eval_types(ret, ctx) # ??? |
|
0 commit comments