`mock.patch` compatible wrapper for `OptionParser`. As of ``mock`` version 1.0.1, when an object uses ``__getattr__`` hooks instead of ``__dict__``, ``patch.__exit__`` tries to delete the attribute it set instead of setting a new one (assuming that the object does not capture ``__se
| 497 | |
| 498 | |
| 499 | class _Mockable(object): |
| 500 | """`mock.patch` compatible wrapper for `OptionParser`. |
| 501 | |
| 502 | As of ``mock`` version 1.0.1, when an object uses ``__getattr__`` |
| 503 | hooks instead of ``__dict__``, ``patch.__exit__`` tries to delete |
| 504 | the attribute it set instead of setting a new one (assuming that |
| 505 | the object does not capture ``__setattr__``, so the patch |
| 506 | created a new attribute in ``__dict__``). |
| 507 | |
| 508 | _Mockable's getattr and setattr pass through to the underlying |
| 509 | OptionParser, and delattr undoes the effect of a previous setattr. |
| 510 | """ |
| 511 | |
| 512 | def __init__(self, options: OptionParser) -> None: |
| 513 | # Modify __dict__ directly to bypass __setattr__ |
| 514 | self.__dict__["_options"] = options |
| 515 | self.__dict__["_originals"] = {} |
| 516 | |
| 517 | def __getattr__(self, name: str) -> Any: |
| 518 | return getattr(self._options, name) |
| 519 | |
| 520 | def __setattr__(self, name: str, value: Any) -> None: |
| 521 | assert name not in self._originals, "don't reuse mockable objects" |
| 522 | self._originals[name] = getattr(self._options, name) |
| 523 | setattr(self._options, name, value) |
| 524 | |
| 525 | def __delattr__(self, name: str) -> None: |
| 526 | setattr(self._options, name, self._originals.pop(name)) |
| 527 | |
| 528 | |
| 529 | class _Option(object): |