Skip to content

Commit 1cd4583

Browse files
Copilotletmaik
andcommitted
Address PR review feedback
- Combine test_mypy_source and test_mypy_tests into single test_mypy function - Remove unnecessary mypy config options and rely on defaults - Remove native module ignores (not needed with TYPE_CHECKING guards) - Add type annotations to all Camera.__init__ fields - Use local variable pattern to ensure _backend is never None after init - Use _closed flag instead of setting _backend to None in close() - Remove assertions that checked for None backend - Exclude test/win-dshow-capture from mypy checking - Add return type annotation to get_pybind_include.__str__ Co-authored-by: letmaik <530988+letmaik@users.noreply.github.com>
1 parent 7255547 commit 1cd4583

File tree

4 files changed

+33
-60
lines changed

4 files changed

+33
-60
lines changed

mypy.ini

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,8 @@
11
[mypy]
2-
# Global options
2+
# Minimum Python version that the codebase should be compatible with
33
python_version = 3.10
4-
warn_return_any = False
5-
warn_unused_configs = True
6-
disallow_untyped_defs = False
7-
check_untyped_defs = True
8-
# Allow implicit Optional (None defaults)
9-
no_implicit_optional = False
10-
11-
# Ignore native extension modules that are built at install time
12-
[mypy-pyvirtualcam._native_windows_obs]
13-
ignore_missing_imports = True
14-
15-
[mypy-pyvirtualcam._native_windows_unity_capture]
16-
ignore_missing_imports = True
17-
18-
[mypy-pyvirtualcam._native_macos_obs_cmioextension]
19-
ignore_missing_imports = True
20-
21-
[mypy-pyvirtualcam._native_macos_obs_dal]
22-
ignore_missing_imports = True
23-
24-
[mypy-pyvirtualcam._native_linux_v4l2loopback]
25-
ignore_missing_imports = True
4+
# Exclude test helper modules with complex setup patterns
5+
exclude = test/win-dshow-capture/
266

277
# Test-specific native modules
288
[mypy-pyvirtualcam_win_dshow_capture.*]
@@ -34,3 +14,7 @@ ignore_missing_imports = True
3414

3515
[mypy-imageio]
3616
ignore_missing_imports = True
17+
18+
[mypy-pybind11]
19+
ignore_missing_imports = True
20+

pyvirtualcam/camera.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Dict, Type, TYPE_CHECKING
1+
from typing import Optional, Dict, Type, TYPE_CHECKING, Callable
22
from abc import ABC, abstractmethod
33
import platform
44
import time
@@ -210,28 +210,29 @@ def __init__(self, width: int, height: int, fps: float, *,
210210
backends = [(backend, BACKENDS[backend])]
211211
else:
212212
backends = list(BACKENDS.items())
213-
self._backend = None
213+
backend_instance: Optional[Backend] = None
214214
errors = []
215215
for name, clazz in backends:
216216
try:
217-
self._backend = clazz(
217+
backend_instance = clazz(
218218
width=width, height=height, fps=fps,
219219
fourcc=encode_fourcc(fmt.value),
220220
device=device,
221221
**kw)
222222
except Exception as e:
223223
errors.append(f"'{name}' backend: {e}")
224224
else:
225-
self._backend_name = name
225+
self._backend_name: str = name
226226
break
227-
if self._backend is None:
227+
if backend_instance is None:
228228
raise RuntimeError('\n'.join(errors))
229-
230-
self._width = width
231-
self._height = height
232-
self._fps = fps
233-
self._fmt = fmt
234-
self._print_fps = print_fps
229+
230+
self._backend: Backend = backend_instance
231+
self._width: int = width
232+
self._height: int = height
233+
self._fps: float = fps
234+
self._fmt: PixelFormat = fmt
235+
self._print_fps: bool = print_fps
235236

236237
frame_shape = FrameShapes[fmt](width, height)
237238
if isinstance(frame_shape, int):
@@ -243,13 +244,14 @@ def check_frame_shape(frame: np.ndarray):
243244
if frame.shape != frame_shape:
244245
raise ValueError(f"unexpected frame shape: {frame.shape} != {frame_shape}")
245246

246-
self._check_frame_shape = check_frame_shape
247+
self._check_frame_shape: Callable[[np.ndarray], None] = check_frame_shape
247248

248-
self._fps_counter = FPSCounter(fps)
249-
self._fps_last_printed = time.perf_counter()
250-
self._frames_sent = 0
251-
self._last_frame_t = time.perf_counter()
252-
self._extra_time_per_frame = 0.0
249+
self._fps_counter: FPSCounter = FPSCounter(fps)
250+
self._fps_last_printed: float = time.perf_counter()
251+
self._frames_sent: int = 0
252+
self._last_frame_t: float = time.perf_counter()
253+
self._extra_time_per_frame: float = 0.0
254+
self._closed: bool = False
253255

254256
def __enter__(self):
255257
return self
@@ -270,7 +272,6 @@ def backend(self) -> str:
270272
def device(self) -> str:
271273
""" The virtual camera device in use.
272274
"""
273-
assert self._backend is not None
274275
return self._backend.device()
275276

276277
@property
@@ -308,7 +309,6 @@ def native_fmt(self) -> Optional[PixelFormat]:
308309
For example, on Windows, a camera device typically
309310
supports multiple formats.
310311
"""
311-
assert self._backend is not None
312312
fourcc = self._backend.native_fourcc()
313313
return PixelFormat(decode_fourcc(fourcc)) if fourcc else None
314314

@@ -324,9 +324,9 @@ def close(self) -> None:
324324
This method is automatically called when using ``with`` or
325325
when this instance goes out of scope.
326326
"""
327-
if self._backend is not None:
327+
if not self._closed:
328328
self._backend.close()
329-
self._backend = None
329+
self._closed = True
330330

331331
def send(self, frame: np.ndarray) -> None:
332332
"""Send a frame to the virtual camera device.
@@ -356,7 +356,6 @@ def send(self, frame: np.ndarray) -> None:
356356
print(s)
357357

358358
frame = np.asarray(frame.reshape(-1), order='C')
359-
assert self._backend is not None
360359
self._backend.send(frame)
361360

362361
@property

test/test_mypy.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,11 @@
33
import sys
44

55

6-
def test_mypy_source():
7-
"""Run mypy on the source folder."""
6+
def test_mypy():
7+
"""Run mypy on the source and test folders."""
88
result = subprocess.run(
9-
[sys.executable, '-m', 'mypy', '--install-types', '--non-interactive', 'pyvirtualcam'],
9+
[sys.executable, '-m', 'mypy', '--install-types', '--non-interactive', 'pyvirtualcam', 'test'],
1010
capture_output=True,
1111
text=True
1212
)
13-
assert result.returncode == 0, f"mypy failed on source folder:\n{result.stdout}\n{result.stderr}"
14-
15-
16-
def test_mypy_tests():
17-
"""Run mypy on the test folder."""
18-
result = subprocess.run(
19-
[sys.executable, '-m', 'mypy', '--install-types', '--non-interactive', 'test'],
20-
capture_output=True,
21-
text=True
22-
)
23-
assert result.returncode == 0, f"mypy failed on test folder:\n{result.stdout}\n{result.stderr}"
13+
assert result.returncode == 0, f"mypy failed:\n{result.stdout}\n{result.stderr}"

test/win-dshow-capture/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class get_pybind_include(object):
1212
until it is actually installed, so that the ``get_include()``
1313
method can be invoked. """
1414

15-
def __str__(self):
15+
def __str__(self) -> str:
1616
import pybind11
1717
return pybind11.get_include()
1818

0 commit comments

Comments
 (0)