| 291 | |
| 292 | |
| 293 | def _exclusive_against(*names: str, **kw: Any) -> Callable[[_Fn], _Fn]: |
| 294 | msgs: Dict[str, str] = kw.pop("msgs", {}) |
| 295 | |
| 296 | defaults: Dict[str, str] = kw.pop("defaults", {}) |
| 297 | |
| 298 | getters: List[Tuple[str, operator.attrgetter[Any], Optional[str]]] = [ |
| 299 | (name, operator.attrgetter(name), defaults.get(name, None)) |
| 300 | for name in names |
| 301 | ] |
| 302 | |
| 303 | @util.decorator |
| 304 | def check(fn: _Fn, *args: Any, **kw: Any) -> Any: |
| 305 | # make pylance happy by not including "self" in the argument |
| 306 | # list |
| 307 | self = args[0] |
| 308 | args = args[1:] |
| 309 | for name, getter, default_ in getters: |
| 310 | if getter(self) is not default_: |
| 311 | msg = msgs.get( |
| 312 | name, |
| 313 | "Method %s() has already been invoked on this %s construct" |
| 314 | % (fn.__name__, self.__class__), |
| 315 | ) |
| 316 | raise exc.InvalidRequestError(msg) |
| 317 | return fn(self, *args, **kw) |
| 318 | |
| 319 | return check |
| 320 | |
| 321 | |
| 322 | def _clone(element, **kw): |