Skip to content

Commit 45c128b

Browse files
committed
Finish reorganizing; fix assertion for duplicated messages
To support the changes, this adds typing-extensions as a test dependency, since we are now importing from it in a test module. But this should probably be required and used conditionally based on whether the Python version has assert_type in its typing module.
1 parent 9d58e6d commit 45c128b

File tree

2 files changed

+50
-49
lines changed

2 files changed

+50
-49
lines changed

test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ pytest-cov
88
pytest-instafail
99
pytest-mock
1010
pytest-sugar
11+
typing-extensions

test/deprecation/test_attributes.py

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,72 @@
44
checks static typing of the code under test. (Running pytest checks dynamic behavior.)
55
"""
66

7-
import importlib
7+
from itertools import groupby
88
from typing import Type
99

1010
import pytest
11+
from typing_extensions import assert_type
1112

1213
import git
1314

1415

15-
def test_cannot_get_undefined() -> None:
16+
def test_cannot_access_undefined() -> None:
17+
"""Accessing a bogus attribute in git remains both a dynamic and static error."""
1618
with pytest.raises(AttributeError):
1719
git.foo # type: ignore[attr-defined]
1820

1921

2022
def test_cannot_import_undefined() -> None:
23+
"""Importing a bogus attribute from git remains both a dynamic and static error."""
2124
with pytest.raises(ImportError):
2225
from git import foo # type: ignore[attr-defined] # noqa: F401
2326

2427

25-
def test_util_alias_members_resolve() -> None:
26-
"""git.index.util members can be accessed via git.util, and mypy recognizes it."""
27-
gu_tfs = git.util.TemporaryFileSwap
28-
from git.index.util import TemporaryFileSwap
28+
def test_util_alias_access() -> None:
29+
"""Accessing util in git works, warns, and mypy verifies it and its attributes."""
30+
# The attribute access should succeed.
31+
with pytest.deprecated_call() as ctx:
32+
util = git.util
2933

30-
def accepts_tfs_type(t: Type[TemporaryFileSwap]) -> None:
31-
pass
34+
# There should be exactly one warning and it should have our util-specific message.
35+
(message,) = [str(entry.message) for entry in ctx]
36+
assert "git.util" in message
37+
assert "git.index.util" in message
38+
assert "should not be relied on" in message
3239

33-
def rejects_tfs_type(t: Type[git.Git]) -> None:
34-
pass
40+
# We check access through the util alias to the TemporaryFileSwap member, since it
41+
# is slightly simpler to validate and reason about than the other public members,
42+
# which are functions (specifically, higher-order functions for use as decorators).
43+
from git.index.util import TemporaryFileSwap
3544

36-
# TODO: When typing_extensions is made a test dependency, use assert_type for this.
37-
accepts_tfs_type(gu_tfs)
38-
rejects_tfs_type(gu_tfs) # type: ignore[arg-type]
45+
assert_type(util.TemporaryFileSwap, Type[TemporaryFileSwap])
3946

40-
assert gu_tfs is TemporaryFileSwap
47+
# This comes after the static assertion, just in case it would affect the inference.
48+
assert util.TemporaryFileSwap is TemporaryFileSwap
4149

4250

43-
def test_util_alias_access_warns() -> None:
51+
def test_util_alias_import() -> None:
52+
"""Importing util from git works, warns, and mypy verifies it and its attributes."""
53+
# The import should succeed.
4454
with pytest.deprecated_call() as ctx:
45-
git.util
55+
from git import util
4656

47-
assert len(ctx) == 1
48-
message = str(ctx[0].message)
57+
# There may be multiple warnings. In CPython there will be currently always be
58+
# exactly two, possibly due to the equivalent of calling hasattr to do a pre-check
59+
# prior to retrieving the attribute for actual use. However, all warnings should
60+
# have the same message, and it should be our util-specific message.
61+
(message,) = {str(entry.message) for entry in ctx}
4962
assert "git.util" in message
5063
assert "git.index.util" in message
5164
assert "should not be relied on" in message
5265

66+
# As above, we check access through the util alias to the TemporaryFileSwap member.
67+
from git.index.util import TemporaryFileSwap
5368

54-
def test_util_alias_import_warns() -> None:
55-
with pytest.deprecated_call() as ctx:
56-
from git import util # noqa: F401
69+
assert_type(util.TemporaryFileSwap, Type[TemporaryFileSwap])
5770

58-
message = str(ctx[0].message)
59-
assert "git.util" in message
60-
assert "git.index.util" in message
61-
assert "should not be relied on" in message
71+
# This comes after the static assertion, just in case it would affect the inference.
72+
assert util.TemporaryFileSwap is TemporaryFileSwap
6273

6374

6475
# Split out util and have all its tests be separate, above.
@@ -71,12 +82,11 @@ def test_util_alias_import_warns() -> None:
7182
git.index.base,
7283
git.index.fun,
7384
git.index.typ,
74-
git.index.util,
7585
)
7686

7787

78-
def test_private_module_alias_access_on_git_module() -> None:
79-
"""Private alias access works, warns, and except for util is a mypy error."""
88+
def test_private_module_alias_access() -> None:
89+
"""Non-util private alias access works, warns, but is a deliberate mypy error."""
8090
with pytest.deprecated_call() as ctx:
8191
assert (
8292
git.head, # type: ignore[attr-defined]
@@ -87,21 +97,16 @@ def test_private_module_alias_access_on_git_module() -> None:
8797
git.base, # type: ignore[attr-defined]
8898
git.fun, # type: ignore[attr-defined]
8999
git.typ, # type: ignore[attr-defined]
90-
git.util,
91100
) == _MODULE_ALIAS_TARGETS
92101

102+
# Each should have warned exactly once, and note what to use instead.
93103
messages = [str(w.message) for w in ctx]
94-
for target, message in zip(_MODULE_ALIAS_TARGETS[:-1], messages[:-1], strict=True):
104+
for target, message in zip(_MODULE_ALIAS_TARGETS, messages, strict=True):
95105
assert message.endswith(f"Use {target.__name__} instead.")
96106

97-
util_message = messages[-1]
98-
assert "git.util" in util_message
99-
assert "git.index.util" in util_message
100-
assert "should not be relied on" in util_message
101107

102-
103-
def test_private_module_alias_import_from_git_module() -> None:
104-
"""Private alias import works, warns, and except for util is a mypy error."""
108+
def test_private_module_alias_import() -> None:
109+
"""Non-util private alias access works, warns, but is a deliberate mypy error."""
105110
with pytest.deprecated_call() as ctx:
106111
from git import head # type: ignore[attr-defined]
107112
from git import log # type: ignore[attr-defined]
@@ -111,7 +116,6 @@ def test_private_module_alias_import_from_git_module() -> None:
111116
from git import base # type: ignore[attr-defined]
112117
from git import fun # type: ignore[attr-defined]
113118
from git import typ # type: ignore[attr-defined]
114-
from git import util
115119

116120
assert (
117121
head,
@@ -122,17 +126,13 @@ def test_private_module_alias_import_from_git_module() -> None:
122126
base,
123127
fun,
124128
typ,
125-
util,
126129
) == _MODULE_ALIAS_TARGETS
127130

128-
# FIXME: This fails because, with imports, multiple consecutive accesses may occur.
129-
# In practice, with CPython, it is always exactly two accesses, the first from the
130-
# equivalent of a hasattr, and the second to fetch the attribute intentionally.
131-
messages = [str(w.message) for w in ctx]
132-
for target, message in zip(_MODULE_ALIAS_TARGETS[:-1], messages[:-1], strict=True):
131+
# Each import may warn multiple times. In CPython there will be currently always be
132+
# exactly two warnings per import, possibly due to the equivalent of calling hasattr
133+
# to do a pre-check prior to retrieving the attribute for actual use. However, for
134+
# each import, all messages should be the same and should note what to use instead.
135+
messages_with_duplicates = [str(w.message) for w in ctx]
136+
messages = [message for message, _ in groupby(messages_with_duplicates)]
137+
for target, message in zip(_MODULE_ALIAS_TARGETS, messages, strict=True):
133138
assert message.endswith(f"Use {target.__name__} instead.")
134-
135-
util_message = messages[-1]
136-
assert "git.util" in util_message
137-
assert "git.index.util" in util_message
138-
assert "should not be relied on" in util_message

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy