Skip to content

Commit e5b21e0

Browse files
committed
Fix mypy warnings in tests that assume Diff.__iter__ can't yield None
1 parent e051a14 commit e5b21e0

File tree

3 files changed

+53
-25
lines changed

3 files changed

+53
-25
lines changed

test/test_diff.py

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
from pygit2 import Diff, Repository
3737
from pygit2.enums import DeltaStatus, DiffFlag, DiffOption, DiffStatsFormat, FileMode
3838

39+
from .utils import diff_safeiter
40+
3941
COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10'
4042
COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c'
4143
COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b'
@@ -177,11 +179,11 @@ def test_diff_empty_index(dirtyrepo: Repository) -> None:
177179
head = repo[repo.lookup_reference('HEAD').resolve().target]
178180

179181
diff = head.tree.diff_to_index(repo.index)
180-
files = [patch.delta.new_file.path for patch in diff]
182+
files = [patch.delta.new_file.path for patch in diff_safeiter(diff)]
181183
assert DIFF_HEAD_TO_INDEX_EXPECTED == files
182184

183185
diff = repo.diff('HEAD', cached=True)
184-
files = [patch.delta.new_file.path for patch in diff]
186+
files = [patch.delta.new_file.path for patch in diff_safeiter(diff)]
185187
assert DIFF_HEAD_TO_INDEX_EXPECTED == files
186188

187189

@@ -190,17 +192,17 @@ def test_workdir_to_tree(dirtyrepo: Repository) -> None:
190192
head = repo[repo.lookup_reference('HEAD').resolve().target]
191193

192194
diff = head.tree.diff_to_workdir()
193-
files = [patch.delta.new_file.path for patch in diff]
195+
files = [patch.delta.new_file.path for patch in diff_safeiter(diff)]
194196
assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files
195197

196198
diff = repo.diff('HEAD')
197-
files = [patch.delta.new_file.path for patch in diff]
199+
files = [patch.delta.new_file.path for patch in diff_safeiter(diff)]
198200
assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files
199201

200202

201203
def test_index_to_workdir(dirtyrepo: Repository) -> None:
202204
diff = dirtyrepo.diff()
203-
files = [patch.delta.new_file.path for patch in diff]
205+
files = [patch.delta.new_file.path for patch in diff_safeiter(diff)]
204206
assert DIFF_INDEX_TO_WORK_EXPECTED == files
205207

206208

@@ -218,15 +220,15 @@ def test_diff_empty_index_bare(barerepo: Repository) -> None:
218220
head = repo[repo.lookup_reference('HEAD').resolve().target]
219221

220222
diff = barerepo.index.diff_to_tree(head.tree)
221-
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
223+
files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)]
222224
assert [x.name for x in head.tree] == files
223225

224226
diff = head.tree.diff_to_index(repo.index)
225-
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
227+
files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)]
226228
assert [x.name for x in head.tree] == files
227229

228230
diff = repo.diff('HEAD', cached=True)
229-
files = [patch.delta.new_file.path.split('/')[0] for patch in diff]
231+
files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)]
230232
assert [x.name for x in head.tree] == files
231233

232234

@@ -236,9 +238,10 @@ def test_diff_tree(barerepo: Repository) -> None:
236238

237239
def _test(diff: Diff) -> None:
238240
assert diff is not None
239-
assert 2 == sum(map(lambda x: len(x.hunks), diff))
241+
assert 2 == sum(map(lambda x: len(x.hunks), diff_safeiter(diff)))
240242

241243
patch = diff[0]
244+
assert patch is not None
242245
hunk = patch.hunks[0]
243246
assert hunk.old_start == 1
244247
assert hunk.old_lines == 1
@@ -268,16 +271,16 @@ def test_diff_empty_tree(barerepo: Repository) -> None:
268271
diff = commit_a.tree.diff_to_tree()
269272

270273
def get_context_for_lines(diff: Diff) -> Iterator[str]:
271-
hunks = chain.from_iterable(map(lambda x: x.hunks, diff))
274+
hunks = chain.from_iterable(map(lambda x: x.hunks, diff_safeiter(diff)))
272275
lines = chain.from_iterable(map(lambda x: x.lines, hunks))
273276
return map(lambda x: x.origin, lines)
274277

275-
entries = [p.delta.new_file.path for p in diff]
278+
entries = [p.delta.new_file.path for p in diff_safeiter(diff)]
276279
assert all(commit_a.tree[x] for x in entries)
277280
assert all('-' == x for x in get_context_for_lines(diff))
278281

279282
diff_swaped = commit_a.tree.diff_to_tree(swap=True)
280-
entries = [p.delta.new_file.path for p in diff_swaped]
283+
entries = [p.delta.new_file.path for p in diff_safeiter(diff_swaped)]
281284
assert all(commit_a.tree[x] for x in entries)
282285
assert all('+' == x for x in get_context_for_lines(diff_swaped))
283286

@@ -294,11 +297,15 @@ def test_diff_tree_opts(barerepo: Repository) -> None:
294297
for flag in [DiffOption.IGNORE_WHITESPACE, DiffOption.IGNORE_WHITESPACE_EOL]:
295298
diff = commit_c.tree.diff_to_tree(commit_d.tree, flag)
296299
assert diff is not None
297-
assert 0 == len(diff[0].hunks)
300+
patch = diff[0]
301+
assert patch is not None
302+
assert 0 == len(patch.hunks)
298303

299304
diff = commit_c.tree.diff_to_tree(commit_d.tree)
300305
assert diff is not None
301-
assert 1 == len(diff[0].hunks)
306+
patch = diff[0]
307+
assert patch is not None
308+
assert 1 == len(patch.hunks)
302309

303310

304311
def test_diff_merge(barerepo: Repository) -> None:
@@ -311,13 +318,14 @@ def test_diff_merge(barerepo: Repository) -> None:
311318

312319
diff_c = commit_b.tree.diff_to_tree(commit_c.tree)
313320
assert diff_c is not None
314-
assert 'b' not in [patch.delta.new_file.path for patch in diff_b]
315-
assert 'b' in [patch.delta.new_file.path for patch in diff_c]
321+
assert 'b' not in [patch.delta.new_file.path for patch in diff_safeiter(diff_b)]
322+
assert 'b' in [patch.delta.new_file.path for patch in diff_safeiter(diff_c)]
316323

317324
diff_b.merge(diff_c)
318-
assert 'b' in [patch.delta.new_file.path for patch in diff_b]
325+
assert 'b' in [patch.delta.new_file.path for patch in diff_safeiter(diff_b)]
319326

320327
patch = diff_b[0]
328+
assert patch is not None
321329
hunk = patch.hunks[0]
322330
assert hunk.old_start == 1
323331
assert hunk.old_lines == 1
@@ -341,6 +349,7 @@ def test_diff_ids(barerepo: Repository) -> None:
341349
commit_a = barerepo[COMMIT_SHA1_1]
342350
commit_b = barerepo[COMMIT_SHA1_2]
343351
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
352+
assert patch is not None
344353
delta = patch.delta
345354
assert delta.old_file.id == '7f129fd57e31e935c6d60a0c794efe4e6927664b'
346355
assert delta.new_file.id == 'af431f20fc541ed6d5afede3e2dc7160f6f01f16'
@@ -358,6 +367,7 @@ def test_hunk_content(barerepo: Repository) -> None:
358367
commit_a = barerepo[COMMIT_SHA1_1]
359368
commit_b = barerepo[COMMIT_SHA1_2]
360369
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
370+
assert patch is not None
361371
hunk = patch.hunks[0]
362372
lines = (f'{x.origin} {x.content}' for x in hunk.lines)
363373
assert HUNK_EXPECTED == ''.join(lines)
@@ -372,11 +382,15 @@ def test_find_similar(barerepo: Repository) -> None:
372382
# ~ Must pass INCLUDE_UNMODIFIED if you expect to emulate
373383
# ~ --find-copies-harder during rename transformion...
374384
diff = commit_a.tree.diff_to_tree(commit_b.tree, DiffOption.INCLUDE_UNMODIFIED)
375-
assert all(x.delta.status != DeltaStatus.RENAMED for x in diff)
376-
assert all(x.delta.status_char() != 'R' for x in diff)
385+
assert all(
386+
patch.delta.status != DeltaStatus.RENAMED for patch in diff_safeiter(diff)
387+
)
388+
assert all(patch.delta.status_char() != 'R' for patch in diff_safeiter(diff))
377389
diff.find_similar()
378-
assert any(x.delta.status == DeltaStatus.RENAMED for x in diff)
379-
assert any(x.delta.status_char() == 'R' for x in diff)
390+
assert any(
391+
patch.delta.status == DeltaStatus.RENAMED for patch in diff_safeiter(diff)
392+
)
393+
assert any(patch.delta.status_char() == 'R' for patch in diff_safeiter(diff))
380394

381395

382396
def test_diff_stats(barerepo: Repository) -> None:
@@ -399,7 +413,7 @@ def test_deltas(barerepo: Repository) -> None:
399413
commit_b = barerepo[COMMIT_SHA1_2]
400414
diff = commit_a.tree.diff_to_tree(commit_b.tree)
401415
deltas = list(diff.deltas)
402-
patches = list(diff)
416+
patches = list(diff_safeiter(diff))
403417
assert len(deltas) == len(patches)
404418
for i, delta in enumerate(deltas):
405419
patch_delta = patches[i].delta
@@ -462,7 +476,7 @@ def test_diff_blobs(emptyrepo: Repository) -> None:
462476
assert diff_all_together.text == PATCH_BLOBS_DEFAULT
463477

464478

465-
def test_diff_unchanged_file_no_patch(testrepo) -> None:
479+
def test_diff_unchanged_file_no_patch(testrepo: Repository) -> None:
466480
repo = testrepo
467481

468482
# Convert hello.txt line endings to CRLF

test/test_repository.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,9 @@ def stash_pathspecs(paths: list[str]) -> bool:
456456
)
457457
stash_commit = testrepo[stash_id].peel(pygit2.Commit)
458458
stash_diff = testrepo.diff(stash_commit.parents[0], stash_commit)
459-
stash_files = set(patch.delta.new_file.path for patch in stash_diff)
459+
stash_files = set(
460+
patch.delta.new_file.path for patch in utils.diff_safeiter(stash_diff)
461+
)
460462
return stash_files == set(paths)
461463

462464
# Stash a modified file

test/utils.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import stat
3131
import sys
3232
import zipfile
33-
from collections.abc import Callable
33+
from collections.abc import Callable, Iterator
3434
from pathlib import Path
3535
from types import TracebackType
3636
from typing import Any, Optional, ParamSpec, TypeVar
@@ -103,6 +103,18 @@ def rmtree(path: str | Path) -> None:
103103
shutil.rmtree(path, onerror=force_rm_handle)
104104

105105

106+
def diff_safeiter(diff: pygit2.Diff) -> Iterator[pygit2.Patch]:
107+
"""
108+
In rare cases, Diff.__iter__ may yield None (see diff_get_patch_byindex).
109+
To make mypy happy, use this iterator instead of Diff.__iter__ to ensure
110+
that all patches in a Diff are valid Patch objects, not None.
111+
"""
112+
for patch in diff:
113+
if patch is None:
114+
raise TypeError('patch is None')
115+
yield patch
116+
117+
106118
class TemporaryRepository:
107119
def __init__(self, name: str, tmp_path: Path) -> None:
108120
self.name = name

0 commit comments

Comments
 (0)