| 232 | """ |
| 233 | |
| 234 | def decorator(func): |
| 235 | if isinstance(func, type): |
| 236 | raise TypeError( |
| 237 | "@deprecate_posargs cannot be applied to a class. (Apply it " |
| 238 | "to the __init__ method.)" |
| 239 | ) |
| 240 | if isinstance(func, classmethod): |
| 241 | raise TypeError("Apply @classmethod before @deprecate_posargs.") |
| 242 | if isinstance(func, staticmethod): |
| 243 | raise TypeError("Apply @staticmethod before @deprecate_posargs.") |
| 244 | |
| 245 | params = signature(func).parameters |
| 246 | num_by_kind = Counter(param.kind for param in params.values()) |
| 247 | |
| 248 | if num_by_kind[inspect.Parameter.VAR_POSITIONAL] > 0: |
| 249 | raise TypeError( |
| 250 | "@deprecate_posargs() cannot be used with variable positional `*args`." |
| 251 | ) |
| 252 | |
| 253 | num_positional_params = ( |
| 254 | num_by_kind[inspect.Parameter.POSITIONAL_ONLY] |
| 255 | + num_by_kind[inspect.Parameter.POSITIONAL_OR_KEYWORD] |
| 256 | ) |
| 257 | num_keyword_only_params = num_by_kind[inspect.Parameter.KEYWORD_ONLY] |
| 258 | if num_keyword_only_params < 1: |
| 259 | raise TypeError( |
| 260 | "@deprecate_posargs() requires at least one keyword-only parameter " |
| 261 | "(after a `*` entry in the parameters list)." |
| 262 | ) |
| 263 | if any( |
| 264 | name not in params or params[name].kind != inspect.Parameter.KEYWORD_ONLY |
| 265 | for name in remappable_names |
| 266 | ): |
| 267 | raise TypeError( |
| 268 | "@deprecate_posargs() requires all remappable_names to be " |
| 269 | "keyword-only parameters." |
| 270 | ) |
| 271 | |
| 272 | num_remappable_args = len(remappable_names) |
| 273 | max_positional_args = num_positional_params + num_remappable_args |
| 274 | |
| 275 | func_name = func.__name__ |
| 276 | if func_name == "__init__": |
| 277 | # In the warning, show "ClassName()" instead of "__init__()". |
| 278 | # The class isn't defined yet, but its name is in __qualname__. |
| 279 | # Some examples of __qualname__: |
| 280 | # - ClassName.__init__ |
| 281 | # - Nested.ClassName.__init__ |
| 282 | # - MyTests.test_case.<locals>.ClassName.__init__ |
| 283 | local_name = func.__qualname__.rsplit("<locals>.", 1)[-1] |
| 284 | class_name = local_name.replace(".__init__", "") |
| 285 | func_name = class_name |
| 286 | |
| 287 | def remap_deprecated_args(args, kwargs): |
| 288 | """ |
| 289 | Move deprecated positional args to kwargs and issue a warning. |
| 290 | Return updated (args, kwargs). |
| 291 | """ |