Skip to content

Commit 6a35261

Browse files
committed
Test that USE_SHELL is unittest.mock.patch patchable
1 parent 40ed842 commit 6a35261

File tree

1 file changed

+36
-0
lines changed

1 file changed

+36
-0
lines changed

test/deprecation/test_cmd_git.py

+36
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import contextlib
5757
import sys
5858
from typing import Generator
59+
import unittest.mock
5960
import warnings
6061

6162
if sys.version_info >= (3, 11):
@@ -250,6 +251,41 @@ def test_use_shell_cannot_set_on_instance(
250251
instance.USE_SHELL = value
251252

252253

254+
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
255+
@pytest.mark.parametrize("original_value", [False, True])
256+
def test_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+
if Git.USE_SHELL is not original_value:
280+
raise RuntimeError(f"Can't set up the test")
281+
new_value = not original_value
282+
283+
with unittest.mock.patch.object(Git, "USE_SHELL", new_value):
284+
assert Git.USE_SHELL is new_value
285+
286+
assert Git.USE_SHELL is original_value
287+
288+
253289
_EXPECTED_DIR_SUBSET = {
254290
"cat_file_all",
255291
"cat_file_header",

0 commit comments

Comments
 (0)