Skip to content

Commit ad2fcfb

Browse files
committed
FilterList: Add boilerplate for mypy
1 parent f722b99 commit ad2fcfb

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

pygit2/_libgit2/ffi.pyi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class GitBlameC:
123123
# incomplete
124124
pass
125125

126+
class GitBlobC:
127+
# incomplete
128+
pass
129+
126130
class GitMergeOptionsC:
127131
file_favor: int
128132
flags: int
@@ -177,6 +181,10 @@ class GitDescribeOptionsC:
177181
class GitDescribeResultC:
178182
pass
179183

184+
class GitFilterListC:
185+
# opaque struct
186+
pass
187+
180188
class GitIndexC:
181189
pass
182190

@@ -264,6 +272,8 @@ def new(a: Literal['git_oid *']) -> GitOidC: ...
264272
@overload
265273
def new(a: Literal['git_blame **']) -> _Pointer[GitBlameC]: ...
266274
@overload
275+
def new(a: Literal['git_blob **']) -> _Pointer[GitBlobC]: ...
276+
@overload
267277
def new(a: Literal['git_clone_options *']) -> GitCloneOptionsC: ...
268278
@overload
269279
def new(a: Literal['git_merge_options *']) -> GitMergeOptionsC: ...
@@ -318,6 +328,8 @@ def new(a: Literal['git_signature *']) -> GitSignatureC: ...
318328
@overload
319329
def new(a: Literal['git_signature **']) -> _Pointer[GitSignatureC]: ...
320330
@overload
331+
def new(a: Literal['git_filter_list **']) -> _Pointer[GitFilterListC]: ...
332+
@overload
321333
def new(a: Literal['int *']) -> int_c: ...
322334
@overload
323335
def new(a: Literal['int64_t *']) -> int64_t: ...

pygit2/filter.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,21 @@
2323
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
2424
# Boston, MA 02110-1301, USA.
2525

26+
from __future__ import annotations
27+
2628
import weakref
2729
from collections.abc import Callable
30+
from typing import TYPE_CHECKING
2831

29-
from ._pygit2 import Blob, FilterSource, Repository
32+
from ._pygit2 import Blob, FilterSource
3033
from .errors import check_error
3134
from .ffi import C, ffi
35+
from .repository import BaseRepository
3236
from .utils import to_bytes
3337

38+
if TYPE_CHECKING:
39+
from ._libgit2.ffi import GitFilterListC
40+
3441

3542
class Filter:
3643
"""
@@ -114,7 +121,9 @@ def close(self, write_next: Callable[[bytes], None]) -> None:
114121

115122

116123
class FilterList:
117-
_all_filter_lists = set()
124+
_all_filter_lists: set[weakref.ReferenceType[FilterList]] = set()
125+
126+
_pointer: GitFilterListC
118127

119128
@classmethod
120129
def _from_c(cls, ptr):
@@ -134,7 +143,14 @@ def _from_c(cls, ptr):
134143

135144
@classmethod
136145
def _is_filter_in_use(cls, name: str) -> bool:
137-
return any(name in ref() for ref in cls._all_filter_lists)
146+
for ref in cls._all_filter_lists:
147+
fl = ref()
148+
assert fl is not None, (
149+
'dead FilterList refs should be purged by weakref callback'
150+
)
151+
if name in fl:
152+
return True
153+
return False
138154

139155
def __contains__(self, name: str) -> bool:
140156
if not isinstance(name, str):
@@ -159,7 +175,7 @@ def apply_to_buffer(self, data: bytes) -> bytes:
159175
finally:
160176
C.git_buf_dispose(buf)
161177

162-
def apply_to_file(self, repo: Repository, path: str) -> bytes:
178+
def apply_to_file(self, repo: BaseRepository, path: str) -> bytes:
163179
"""
164180
Apply a filter list to the contents of a file on disk.
165181
Return the filtered contents.

test/test_filter.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,22 @@ def test_filterlist_crlf(testrepo: Repository) -> None:
152152
assert 'crlf' in fl
153153

154154
with pytest.raises(TypeError):
155-
1234 in fl
155+
# Incorrect on purpose to raise TypeError
156+
1234 in fl # type: ignore
156157

157158

158159
def test_filterlist_crlf_clean(testrepo: Repository) -> None:
159160
testrepo.config['core.autocrlf'] = True
160161
fl = testrepo.load_filter_list('whatever.txt', mode=FilterMode.CLEAN)
162+
assert fl is not None
161163
filtered = fl.apply_to_buffer(b'hello\r\nworld\r\n')
162164
assert filtered == b'hello\nworld\n'
163165

164166

165167
def test_filterlist_crlf_smudge(testrepo: Repository) -> None:
166168
testrepo.config['core.autocrlf'] = True
167169
fl = testrepo.load_filter_list('whatever.txt', mode=FilterMode.SMUDGE)
170+
assert fl is not None
168171
filtered = fl.apply_to_buffer(b'hello\nworld\n')
169172
assert filtered == b'hello\r\nworld\r\n'
170173

@@ -197,6 +200,7 @@ def test_filterlist_rot13_apply_to_buffer(
197200
testrepo: Repository, rot13_filter: Filter
198201
) -> None:
199202
fl = testrepo.load_filter_list('whatever.txt')
203+
assert fl is not None
200204
filtered = fl.apply_to_buffer(b'bye world\n')
201205
assert filtered == b'olr jbeyq\n'
202206

@@ -205,6 +209,7 @@ def test_filterlist_rot13_apply_to_file(
205209
testrepo: Repository, rot13_filter: Filter
206210
) -> None:
207211
fl = testrepo.load_filter_list('bye.txt')
212+
assert fl is not None
208213
filtered = fl.apply_to_file(testrepo, 'bye.txt')
209214
assert filtered == b'olr jbeyq\n'
210215

@@ -213,7 +218,9 @@ def test_filterlist_rot13_apply_to_blob(
213218
testrepo: Repository, rot13_filter: Filter
214219
) -> None:
215220
fl = testrepo.load_filter_list('whatever.txt')
221+
assert fl is not None
216222
blob_oid = testrepo.create_blob(b'bye world\n')
217223
blob = testrepo[blob_oid]
224+
assert isinstance(blob, Blob)
218225
filtered = fl.apply_to_blob(blob)
219226
assert filtered == b'olr jbeyq\n'

0 commit comments

Comments
 (0)