|
| 1 | +# Copyright 2026 The PyAthena authors |
| 2 | +# |
| 3 | +# Licensed under the MIT License. |
| 4 | +# See LICENSE or https://opensource.org/licenses/MIT. |
| 5 | +# |
| 6 | +# SPDX-License-Identifier: MIT |
| 7 | + |
| 8 | +"""Validation of keyword names forwarded by cursors.""" |
| 9 | + |
| 10 | +from collections.abc import Callable, Collection |
| 11 | +from inspect import Parameter, signature |
| 12 | +from typing import Any |
| 13 | + |
| 14 | + |
| 15 | +def keyword_parameters(func: Callable[..., Any]) -> set[str]: |
| 16 | + """Return the explicitly named parameters that accept keyword arguments. |
| 17 | +
|
| 18 | + Args: |
| 19 | + func: The callable whose signature to inspect. |
| 20 | +
|
| 21 | + Returns: |
| 22 | + Its positional-or-keyword and keyword-only parameter names. |
| 23 | + """ |
| 24 | + return { |
| 25 | + name |
| 26 | + for name, parameter in signature(func).parameters.items() |
| 27 | + if parameter.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) |
| 28 | + } |
| 29 | + |
| 30 | + |
| 31 | +def constructor_keyword_parameters(cls: type[Any]) -> set[str]: |
| 32 | + """Return explicit constructor keyword names across a class's MRO. |
| 33 | +
|
| 34 | + Args: |
| 35 | + cls: The class whose forwarding constructors to inspect. |
| 36 | +
|
| 37 | + Returns: |
| 38 | + The named constructor parameters, excluding ``self``. |
| 39 | + """ |
| 40 | + names: set[str] = set() |
| 41 | + for base in cls.__mro__: |
| 42 | + if "__init__" in vars(base): |
| 43 | + names.update(keyword_parameters(vars(base)["__init__"])) |
| 44 | + return names - {"self"} |
| 45 | + |
| 46 | + |
| 47 | +def validate_kwargs(method: str, kwargs: dict[str, Any], allowed: Collection[str] = ()) -> None: |
| 48 | + """Reject the first keyword name that a cursor does not support. |
| 49 | +
|
| 50 | + Args: |
| 51 | + method: The method name included in the error. |
| 52 | + kwargs: Extra keyword arguments given to the method. |
| 53 | + allowed: The extra keyword names the method supports. |
| 54 | +
|
| 55 | + Raises: |
| 56 | + TypeError: If a keyword name is not allowed. |
| 57 | + """ |
| 58 | + for name in kwargs: |
| 59 | + if name not in allowed: |
| 60 | + raise TypeError(f"{method}() got an unexpected keyword argument '{name}'") |
0 commit comments