Skip to content

Commit 3c0f496

Browse files
committed
Continue test refactor
1 parent 1705772 commit 3c0f496

File tree

12 files changed

+251
-226
lines changed

12 files changed

+251
-226
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fate-suite:
3030
lint:
3131
$(PIP) install -U black isort flake8 flake8-pyproject pillow numpy mypy==1.11.2
3232
black --check av examples tests setup.py
33-
flake8 av examples tests
33+
flake8 av
3434
isort --check-only --diff av examples tests
3535
mypy av tests
3636

av/__init__.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,62 @@
1515
+ os.environ["PATH"]
1616
)
1717

18-
# MUST import the core before anything else in order to initalize the underlying
18+
# MUST import the core before anything else in order to initialize the underlying
1919
# library that is being wrapped.
2020
from av._core import time_base, library_versions
2121

2222
# Capture logging (by importing it).
2323
from av import logging
2424

25-
# For convenience, IMPORT ALL OF THE THINGS (that are constructable by the user).
25+
# For convenience, import all common attributes.
2626
from av.about import __version__
27+
from av.audio.codeccontext import AudioCodecContext
2728
from av.audio.fifo import AudioFifo
2829
from av.audio.format import AudioFormat
2930
from av.audio.frame import AudioFrame
3031
from av.audio.layout import AudioLayout
3132
from av.audio.resampler import AudioResampler
33+
from av.audio.stream import AudioStream
3234
from av.bitstream import BitStreamFilterContext, bitstream_filters_available
3335
from av.codec.codec import Codec, codecs_available
3436
from av.codec.context import CodecContext
3537
from av.container import open
3638
from av.format import ContainerFormat, formats_available
3739
from av.packet import Packet
3840
from av.error import * # noqa: F403; This is limited to exception types.
41+
from av.video.codeccontext import VideoCodecContext
3942
from av.video.format import VideoFormat
4043
from av.video.frame import VideoFrame
44+
from av.video.stream import VideoStream
4145

4246
# Backwards compatibility
4347
AVError = FFmpegError # noqa: F405
4448

49+
__all__ = (
50+
"time_base",
51+
"library_versions",
52+
"AudioCodecContext",
53+
"AudioFifo",
54+
"AudioFormat",
55+
"AudioFrame",
56+
"AudioLayout",
57+
"AudioResampler",
58+
"AudioStream",
59+
"BitStreamFilterContext",
60+
"bitstream_filters_available",
61+
"Codec",
62+
"codecs_available",
63+
"CodecContext",
64+
"open",
65+
"ContainerFormat",
66+
"formats_available",
67+
"Packet",
68+
"VideoCodecContext",
69+
"VideoFormat",
70+
"VideoFrame",
71+
"VideoStream",
72+
)
73+
4574

4675
def get_include() -> str:
4776
"""

av/container/core.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from numbers import Real
1+
from fractions import Fraction
22
from pathlib import Path
33
from types import TracebackType
44
from typing import Any, Callable, Literal, Type, overload
@@ -10,6 +10,8 @@ from .input import InputContainer
1010
from .output import OutputContainer
1111
from .streams import StreamContainer
1212

13+
Real = int | float | Fraction
14+
1315
class Flags(EnumFlag):
1416
GENPTS: int
1517
IGNIDX: int

tests/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
except ImportError:
1515
has_pillow = False
1616

17+
__all__ = ("fate_suite",)
18+
1719

1820
is_windows = os.name == "nt"
1921
skip_tests = frozenset(os.environ.get("PYAV_SKIP_TESTS", "").split(","))

tests/test_codec_context.py

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,48 @@
22

33
import os
44
from fractions import Fraction
5+
from typing import Iterator, TypedDict, overload
56
from unittest import SkipTest
67

78
import av
8-
from av import AudioLayout, AudioResampler, Codec, Packet
9+
from av import (
10+
AudioCodecContext,
11+
AudioFrame,
12+
AudioLayout,
13+
AudioResampler,
14+
Codec,
15+
Packet,
16+
VideoCodecContext,
17+
VideoFrame,
18+
)
919
from av.codec.codec import UnknownCodecError
1020
from av.video.frame import PictureType
1121

1222
from .common import TestCase, fate_suite
1323

1424

15-
def iter_raw_frames(path, packet_sizes, ctx):
25+
class Options(TypedDict, total=False):
26+
b: str
27+
crf: str
28+
pix_fmt: str
29+
width: int
30+
height: int
31+
max_frames: int
32+
time_base: Fraction
33+
gop_size: int
34+
35+
36+
@overload
37+
def iter_raw_frames(
38+
path: str, packet_sizes: list, ctx: VideoCodecContext
39+
) -> Iterator[VideoFrame]: ...
40+
@overload
41+
def iter_raw_frames(
42+
path: str, packet_sizes: list, ctx: AudioCodecContext
43+
) -> Iterator[AudioFrame]: ...
44+
def iter_raw_frames(
45+
path: str, packet_sizes: list, ctx: VideoCodecContext | AudioCodecContext
46+
) -> Iterator[VideoFrame | AudioFrame]:
1647
with open(path, "rb") as f:
1748
for i, size in enumerate(packet_sizes):
1849
packet = Packet(size)
@@ -244,24 +275,24 @@ def image_sequence_encode(self, codec_name: str) -> None:
244275
assert frame.height == height
245276
assert frame.format.name == pix_fmt
246277

247-
def test_encoding_h264(self):
278+
def test_encoding_h264(self) -> None:
248279
self.video_encoding("h264", {"crf": "19"})
249280

250-
def test_encoding_mpeg4(self):
281+
def test_encoding_mpeg4(self) -> None:
251282
self.video_encoding("mpeg4")
252283

253-
def test_encoding_xvid(self):
284+
def test_encoding_xvid(self) -> None:
254285
self.video_encoding("mpeg4", codec_tag="xvid")
255286

256-
def test_encoding_mpeg1video(self):
287+
def test_encoding_mpeg1video(self) -> None:
257288
self.video_encoding("mpeg1video")
258289

259-
def test_encoding_dvvideo(self):
260-
options = {"pix_fmt": "yuv411p", "width": 720, "height": 480}
290+
def test_encoding_dvvideo(self) -> None:
291+
options: Options = {"pix_fmt": "yuv411p", "width": 720, "height": 480}
261292
self.video_encoding("dvvideo", options)
262293

263-
def test_encoding_dnxhd(self):
264-
options = {
294+
def test_encoding_dnxhd(self) -> None:
295+
options: Options = {
265296
"b": "90M", # bitrate
266297
"pix_fmt": "yuv422p",
267298
"width": 1920,
@@ -271,7 +302,12 @@ def test_encoding_dnxhd(self):
271302
}
272303
self.video_encoding("dnxhd", options)
273304

274-
def video_encoding(self, codec_name, options={}, codec_tag=None):
305+
def video_encoding(
306+
self,
307+
codec_name: str,
308+
options: Options = {},
309+
codec_tag: str | None = None,
310+
) -> None:
275311
try:
276312
codec = Codec(codec_name, "w")
277313
except UnknownCodecError:
@@ -280,21 +316,23 @@ def video_encoding(self, codec_name, options={}, codec_tag=None):
280316
container = av.open(fate_suite("h264/interlaced_crop.mp4"))
281317
video_stream = container.streams.video[0]
282318

319+
assert video_stream.time_base is not None
320+
283321
pix_fmt = options.pop("pix_fmt", "yuv420p")
284322
width = options.pop("width", 640)
285323
height = options.pop("height", 480)
286324
max_frames = options.pop("max_frames", 50)
287325
time_base = options.pop("time_base", video_stream.time_base)
288326
gop_size = options.pop("gop_size", 20)
289327

290-
ctx = codec.create()
328+
ctx = codec.create("video")
291329
ctx.width = width
292330
ctx.height = height
293331
ctx.time_base = time_base
294332
ctx.framerate = 1 / ctx.time_base
295333
ctx.pix_fmt = pix_fmt
296334
ctx.gop_size = gop_size
297-
ctx.options = options # TODO
335+
ctx.options = options # type: ignore
298336
if codec_tag:
299337
ctx.codec_tag = codec_tag
300338
ctx.open()
@@ -326,7 +364,7 @@ def video_encoding(self, codec_name, options={}, codec_tag=None):
326364
if codec_name == "libx264":
327365
dec_codec_name = "h264"
328366

329-
ctx = av.Codec(dec_codec_name, "r").create()
367+
ctx = av.Codec(dec_codec_name, "r").create("video")
330368
ctx.open()
331369

332370
keyframe_indices = []
@@ -341,7 +379,7 @@ def video_encoding(self, codec_name, options={}, codec_tag=None):
341379

342380
assert frame_count == decoded_frame_count
343381

344-
self.assertIsInstance(
382+
assert isinstance(
345383
all(keyframe_index for keyframe_index in keyframe_indices), int
346384
)
347385
decoded_gop_sizes = [
@@ -350,7 +388,7 @@ def video_encoding(self, codec_name, options={}, codec_tag=None):
350388
if codec_name in ("dvvideo", "dnxhd") and all(
351389
i == 1 for i in decoded_gop_sizes
352390
):
353-
raise SkipTest()
391+
raise SkipTest("I'm not sure why we skip this actually.")
354392
for i in decoded_gop_sizes:
355393
assert i == gop_size
356394

tests/test_decode.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ def test_decode_audio_sample_count(self) -> None:
5151
assert audio_stream.duration is not None
5252
assert audio_stream.time_base is not None
5353
total_samples = (
54-
audio_stream.duration * audio_stream.sample_rate.numerator
55-
) / audio_stream.time_base.denominator
54+
audio_stream.duration
55+
* audio_stream.sample_rate.numerator
56+
/ audio_stream.time_base.denominator
57+
)
5658
assert sample_count == total_samples
5759

5860
def test_decoded_time_base(self):
@@ -125,11 +127,9 @@ def test_decode_close_then_use(self):
125127
except AssertionError:
126128
pass
127129

128-
def test_flush_decoded_video_frame_count(self):
130+
def test_flush_decoded_video_frame_count(self) -> None:
129131
container = av.open(fate_suite("h264/interlaced_crop.mp4"))
130-
video_stream = next(s for s in container.streams if s.type == "video")
131-
132-
self.assertIs(video_stream, container.streams.video[0])
132+
video_stream = container.streams.video[0]
133133

134134
# Decode the first GOP, which requires a flush to get all frames
135135
have_keyframe = False
@@ -148,11 +148,11 @@ def test_flush_decoded_video_frame_count(self):
148148
output_count += 1
149149

150150
# Check the test works as expected and requires a flush
151-
self.assertLess(output_count, input_count)
151+
assert output_count < input_count
152152

153153
for frame in video_stream.decode(None):
154154
# The Frame._time_base is not set by PyAV
155-
self.assertIsNone(frame.time_base)
155+
assert frame.time_base is None
156156
output_count += 1
157157

158158
assert output_count == input_count

tests/test_enums.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ def define_foobar(self, **kwargs):
1515
def test_basics(self):
1616
cls = self.define_foobar()
1717

18-
self.assertIsInstance(cls, EnumType)
18+
assert isinstance(cls, EnumType)
1919

2020
foo = cls.FOO
2121

22-
self.assertIsInstance(foo, cls)
23-
self.assertEqual(foo.name, "FOO")
24-
self.assertEqual(foo.value, 1)
25-
26-
self.assertNotIsInstance(foo, PickleableFooBar)
22+
assert isinstance(foo, cls)
23+
assert foo.name == "FOO" and foo.value == 1
24+
assert not isinstance(foo, PickleableFooBar)
2725

2826
def test_access(self):
2927
cls = self.define_foobar()
@@ -35,9 +33,9 @@ def test_access(self):
3533
assert foo1 is foo3
3634
assert foo1 is foo4
3735

38-
self.assertIn(foo1, cls)
39-
self.assertIn("FOO", cls)
40-
self.assertIn(1, cls)
36+
assert foo1 in cls
37+
assert "FOO" in cls
38+
assert 1 in cls
4139

4240
self.assertRaises(KeyError, lambda: cls["not a foo"])
4341
self.assertRaises(KeyError, lambda: cls[10])
@@ -53,12 +51,12 @@ def test_casting(self):
5351
self.assertEqual(repr(foo), "<tests.test_enums.Foobar:FOO(0x1)>")
5452

5553
str_foo = str(foo)
56-
self.assertIsInstance(str_foo, str)
57-
self.assertEqual(str_foo, "FOO")
54+
assert isinstance(str_foo, str)
55+
assert str_foo == "FOO"
5856

5957
int_foo = int(foo)
60-
self.assertIsInstance(int_foo, int)
61-
self.assertEqual(int_foo, 1)
58+
assert isinstance(int_foo, int)
59+
assert int_foo == 1
6260

6361
def test_iteration(self):
6462
cls = self.define_foobar()

0 commit comments

Comments
 (0)