|
1 | 1 | import sys |
2 | 2 | import typing as t |
3 | | -from collections.abc import Iterable, Sequence |
| 3 | +from collections.abc import Iterable, Sequence, Callable |
4 | 4 | from functools import lru_cache |
5 | 5 | from string.templatelib import Interpolation, Template |
6 | 6 | from dataclasses import dataclass |
7 | 7 |
|
8 | 8 | from markupsafe import Markup |
9 | 9 |
|
10 | | -from .callables import get_callable_info |
| 10 | +from .callables import get_callable_info, CallableInfo |
11 | 11 | from .format import format_interpolation as base_format_interpolation |
12 | 12 | from .format import format_template |
13 | 13 | from .nodes import Comment, DocumentType, Element, Fragment, Node, Text |
@@ -437,6 +437,39 @@ def _kebab_to_snake(name: str) -> str: |
437 | 437 | return name.replace("-", "_").lower() |
438 | 438 |
|
439 | 439 |
|
| 440 | +def _prep_component_kwargs( |
| 441 | + callable_info: CallableInfo, |
| 442 | + attrs: AttributesDict, |
| 443 | + system: dict[str, object], |
| 444 | + kebab_to_snake: Callable[[str], str] = _kebab_to_snake, |
| 445 | +): |
| 446 | + if callable_info.requires_positional: |
| 447 | + raise TypeError( |
| 448 | + "Component callables cannot have required positional arguments." |
| 449 | + ) |
| 450 | + |
| 451 | + kwargs: AttributesDict = {} |
| 452 | + |
| 453 | + # Add all supported attributes |
| 454 | + for attr_name, attr_value in attrs.items(): |
| 455 | + snake_name = kebab_to_snake(attr_name) |
| 456 | + if snake_name in callable_info.named_params or callable_info.kwargs: |
| 457 | + kwargs[snake_name] = attr_value |
| 458 | + |
| 459 | + for attr_name, attr_value in system.items(): |
| 460 | + if attr_name in callable_info.named_params or callable_info.kwargs: |
| 461 | + kwargs[attr_name] = attr_value |
| 462 | + |
| 463 | + # Check to make sure we've fully satisfied the callable's requirements |
| 464 | + missing = callable_info.required_named_params - kwargs.keys() |
| 465 | + if missing: |
| 466 | + raise TypeError( |
| 467 | + f"Missing required parameters for component: {', '.join(missing)}" |
| 468 | + ) |
| 469 | + |
| 470 | + return kwargs |
| 471 | + |
| 472 | + |
440 | 473 | def _invoke_component( |
441 | 474 | attrs: AttributesDict, |
442 | 475 | children: list[Node], # TODO: why not TNode, though? |
@@ -477,29 +510,9 @@ def _invoke_component( |
477 | 510 | ) |
478 | 511 | callable_info = get_callable_info(value) |
479 | 512 |
|
480 | | - if callable_info.requires_positional: |
481 | | - raise TypeError( |
482 | | - "Component callables cannot have required positional arguments." |
483 | | - ) |
484 | | - |
485 | | - kwargs: AttributesDict = {} |
486 | | - |
487 | | - # Add all supported attributes |
488 | | - for attr_name, attr_value in attrs.items(): |
489 | | - snake_name = _kebab_to_snake(attr_name) |
490 | | - if snake_name in callable_info.named_params or callable_info.kwargs: |
491 | | - kwargs[snake_name] = attr_value |
492 | | - |
493 | | - # Add children if appropriate |
494 | | - if "children" in callable_info.named_params or callable_info.kwargs: |
495 | | - kwargs["children"] = tuple(children) |
496 | | - |
497 | | - # Check to make sure we've fully satisfied the callable's requirements |
498 | | - missing = callable_info.required_named_params - kwargs.keys() |
499 | | - if missing: |
500 | | - raise TypeError( |
501 | | - f"Missing required parameters for component: {', '.join(missing)}" |
502 | | - ) |
| 513 | + kwargs = _prep_component_kwargs( |
| 514 | + callable_info, attrs, system={"children": tuple(children)} |
| 515 | + ) |
503 | 516 |
|
504 | 517 | result = value(**kwargs) |
505 | 518 | return _node_from_value(result) |
|
0 commit comments