8000 Start on test module about Git.USE_SHELL and Git attributes · gitpython-developers/GitPython@7ab27c5 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7ab27c5

Browse files
committed
Start on test module about Git.USE_SHELL and Git attributes
1 parent dffa930 commit 7ab27c5

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

test/deprecation/test_cmd_git.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
"""Tests for static and dynamic characteristics of Git class and instance attributes.
2+
3+
Currently this all relates to the deprecated :class:`Git.USE_SHELL` class attribute,
4+
which can also be accessed through instances. Some tests directly verify its behavior,
5+
including deprecation warnings, while others verify that other aspects of attribute
6+
access are not inadvertently broken by mechanisms introduced to issue the warnings.
7+
"""
8+
9+
import contextlib
10+
import sys
11+
from typing import Generator
12+
13+
if sys.version_info >= (3, 11):
14+
from typing import assert_type
15+
else:
16+
from typing_extensions import assert_type
17+
18+
import pytest
19+
20+
from git.cmd import Git
21+
22+
_USE_SHELL_DEPRECATED_FRAGMENT = "Git.USE_SHELL is deprecated"
23+
"""Text contained in all USE_SHELL deprecation warnings, and starting most of them."""
24+
25+
_USE_SHELL_DANGEROUS_FRAGMENT = "Setting Git.USE_SHELL to True is unsafe and insecure"
26+
"""Beginning text of USE_SHELL deprecation warnings when USE_SHELL is set True."""
27+
28+
29+
@pytest.fixture
30+
def reset_backing_attribute() -> Generator[None, None, None]:
31+
"""Fixture to reset the private ``_USE_SHELL`` attribute.
32+
33+
This is used to decrease the likelihood of state changes leaking out and affecting
34+
other tests. But the goal is not to assert that ``_USE_SHELL`` is used, nor anything
35+
about how or when it is used, which is an implementation detail subject to change.
36+
37+
This is possible but inelegant to do with pytest's monkeypatch fixture, which only
38+
restores attributes that it has previously been used to change, create, or remove.
39+
"""
40+
no_value = object()
41+
try:
42+
old_value = Git._USE_SHELL
43+
except AttributeError:
44+
old_value = no_value
45+
46+
yield
47+
48+
if old_value is no_value:
49+
with contextlib.suppress(AttributeError):
50+
del Git._USE_SHELL
51+
else:
52+
Git._USE_SHELL = old_value
53+
54+
55+
def test_cannot_access_undefined_on_git_class() -> None:
56+
"""Accessing a bogus attribute on the Git class remains a dynamic and static error.
57+
58+
This differs from Git instances, where most attribute names will dynamically
59+
synthesize a "bound method" that runs a git subcommand when called.
60+
"""
61+
with pytest.raises(AttributeError):
62+
Git.foo # type: ignore[attr-defined]
63+
64+
65+
def test_get_use_shell_on_class_default() -> None:
66+
"""USE_SHELL can be read as a class attribute, defaulting to False and warning."""
67+
with pytest.deprecated_call() as ctx:
68+
use_shell = Git.USE_SHELL
69+
70+
(message,) = [str(entry.message) for entry in ctx] # Exactly one warning.
71+
assert message.startswith(_USE_SHELL_DEPRECATED_FRAGMENT)
72+
73+
assert_type(use_shell, bool)
74+
75+
# This comes after the static assertion, just in case it would affect the inference.
76+
assert not use_shell
77+
78+
79+
# FIXME: More robustly check that each operation really issues exactly one deprecation
80+
# warning, even if this requires relying more on reset_backing_attribute doing its job.
81+
def test_use_shell_on_class(reset_backing_attribute) -> None:
82+
"""USE_SHELL can be written and re-read as a class attribute, always warning."""
83+
# We assert in a "safe" order, using reset_backing_attribute only as a backstop.
84+
with pytest.deprecated_call() as ctx:
85+
Git.USE_SHELL = True
86+
set_value = Git.USE_SHELL
87+
Git.USE_SHELL = False
88+
reset_value = Git.USE_SHELL
89+
90+
# The attribute should take on the values set to it.
91+
assert set_value is True
92+
assert reset_value is False
93+
94+
messages = [str(entry.message) for entry in ctx]
95+
set_message, check_message, reset_message, recheck_message = messages
96+
97+
# Setting it to True should produce the special warning for that.
98+
assert _USE_SHELL_DEPRECATED_FRAGMENT in set_message
99+
assert set_message.startswith(_USE_SHELL_DANGEROUS_FRAGMENT)
100+
101+
# All other operations should produce a usual warning.
102+
assert check_message.startswith(_USE_SHELL_DEPRECATED_FRAGMENT)
103+
assert reset_message.startswith(_USE_SHELL_DEPRECATED_FRAGMENT)
104+
assert recheck_message.startswith(_USE_SHELL_DEPRECATED_FRAGMENT)
105+
106+
107+
# FIXME: Test behavior on instances (where we can get but not set).
108+
109+
# FIXME: Test behavior with multiprocessing (the attribute needs to pickle properly).
110+
111+
112+
_EXPECTED_DIR_SUBSET = {
113+
"cat_file_all",
114+
"cat_file_header",
115+
"GIT_PYTHON_TRACE",
116+
"USE_SHELL", # The attribute we get deprecation warnings for.
117+
"GIT_PYTHON_GIT_EXECUTABLE",
118+
"refresh",
119+
"is_cygwin",
120+
"polish_url",
121+
"check_unsafe_protocols",
122+
"check_unsafe_options",
123+
"AutoInterrupt",
124+
"CatFileContentStream",
125+
"__init__",
126+
"__getattr__",
127+
"set_persistent_git_options",
128+
"working_dir",
129+
"version_info",
130+
"execute",
131+
"environment",
132+
"update_environment",
133+
"custom_environment",
134+
"transform_kwarg",
135+
"transform_kwargs",
136+
"__call__",
137+
"_call_process", # Not currently considered public, but unlikely to change.
138+
"get_object_header",
139+
"get_object_data",
140+
"stream_object_data",
141+
"clear_cache",
142+
}
143+
"""Some stable attributes dir() should include on the Git class and its instances.
144+
145+
This is intentionally incomplete, but includes substantial variety. Most importantly, it
146+
includes both ``USE_SHELL`` and a wide sampling of other attributes.
147+
"""
148+
149+
150+
def test_class_dir() -> None:
151+
"""dir() on the Git class includes its statically known attributes.
152+
153+
This tests that the mechanism that adds dynamic behavior to USE_SHELL accesses so
154+
that all accesses issue warnings does not break dir() for the class, neither for
155+
USE_SHELL nor for ordinary (non-deprecated) attributes.
156+
"""
157+
actual = set(dir(Git))
158+
assert _EXPECTED_DIR_SUBSET <= actual
159+
160+
161+
def test_instance_dir() -> None:
162+
"""dir() on Git objects includes its statically known attributes.
163+
164+
This is like test_class_dir, but for Git instance rather than the class itself.
165+
"""
166+
instance = Git()
167+
actual = set(dir(instance))
168+
assert _EXPECTED_DIR_SUBSET <= actual

0 commit comments

Comments
 (0)
0