|
56 | 56 | importcontextlib
|
57 | 57 | importsys
|
58 | 58 | fromtypingimportGenerator
|
| 59 | +importunittest.mock |
59 | 60 | importwarnings
|
60 | 61 |
|
61 | 62 | ifsys.version_info>= (3,11):
|
@@ -250,6 +251,41 @@ def test_use_shell_cannot_set_on_instance(
|
250 | 251 | instance.USE_SHELL=value
|
251 | 252 |
|
252 | 253 |
|
| 254 | +@pytest.mark.filterwarnings("ignore::DeprecationWarning") |
| 255 | +@pytest.mark.parametrize("original_value", [False,True]) |
| 256 | +deftest_use_shell_is_mock_patchable_on_class_as_object_attribute( |
| 257 | +original_value:bool, |
| 258 | +restore_use_shell_state:None, |
| 259 | +)->None: |
| 260 | +"""Asymmetric patching looking up USE_SHELL in ``__dict__`` doesn't corrupt state. |
| 261 | +
|
| 262 | + Code using GitPython may temporarily set Git.USE_SHELL to a different value. Ideally |
| 263 | + it does not use unittest.mock.patch to do so, because that makes subtle assumptions |
| 264 | + about the relationship between attributes and dictionaries. If the attribute can be |
| 265 | + retrieved from the ``__dict__`` rather than directly, that value is assumed the |
| 266 | + correct one to restore, even by a normal setattr. |
| 267 | +
|
| 268 | + The effect is that some ways of simulating a class attribute with added behavior can |
| 269 | + cause a descriptor, such as a property, to be set to its own backing attribute |
| 270 | + during unpatching; then subsequent reads raise RecursionError. This happens if both |
| 271 | + (a) setting it on the class is customized in a metaclass and (b) getting it on |
| 272 | + instances is customized with a descriptor (such as a property) in the class itself. |
| 273 | +
|
| 274 | + Although ideally code outside GitPython would not rely on being able to patch |
| 275 | + Git.USE_SHELL with unittest.mock.patch, the technique is widespread. Thus, USE_SHELL |
| 276 | + should be implemented in some way compatible with it. This test checks for that. |
| 277 | + """ |
| 278 | +Git.USE_SHELL=original_value |
| 279 | +ifGit.USE_SHELLisnotoriginal_value: |
| 280 | +raiseRuntimeError(f"Can't set up the test") |
| 281 | +new_value=notoriginal_value |
| 282 | + |
| 283 | +withunittest.mock.patch.object(Git,"USE_SHELL",new_value): |
| 284 | +assertGit.USE_SHELLisnew_value |
| 285 | + |
| 286 | +assertGit.USE_SHELLisoriginal_value |
| 287 | + |
| 288 | + |
253 | 289 | _EXPECTED_DIR_SUBSET= {
|
254 | 290 | "cat_file_all",
|
255 | 291 | "cat_file_header",
|
|