Skip to content

Commit a6cd9c0

Browse files
authored
Update Python type annotations to modern syntax and types (#1100)
1 parent 4e412b7 commit a6cd9c0

20 files changed

+261
-262
lines changed

examples/decoding/basic_example.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
# plotting utility. You can ignore that part and jump right below to
1919
# :ref:`creating_decoder`.
2020

21-
from typing import Optional
2221
import torch
2322
import requests
2423

@@ -33,7 +32,7 @@
3332
raw_video_bytes = response.content
3433

3534

36-
def plot(frames: torch.Tensor, title : Optional[str] = None):
35+
def plot(frames: torch.Tensor, title: str | None = None):
3736
try:
3837
from torchvision.utils import make_grid
3938
from torchvision.transforms.v2.functional import to_pil_image

examples/decoding/custom_frame_mappings.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,15 @@
8282

8383
# Lets define a simple function to run ffprobe on a video's first stream index, then writes the results in output_json_path.
8484
def generate_frame_mappings(video_path, output_json_path, stream_index):
85-
ffprobe_cmd = ["ffprobe", "-i", f"{video_path}", "-select_streams", f"{stream_index}", "-show_frames", "-show_entries", "frame=pts,duration,key_frame", "-of", "json"]
85+
ffprobe_cmd = [
86+
"ffprobe",
87+
"-i", f"{video_path}",
88+
"-select_streams", f"{stream_index}",
89+
"-show_frames",
90+
"-show_entries",
91+
"frame=pts,duration,key_frame",
92+
"-of", "json",
93+
]
8694
print(f"Running ffprobe:\n{' '.join(ffprobe_cmd)}\n")
8795
ffprobe_result = subprocess.run(ffprobe_cmd, check=True, capture_output=True, text=True)
8896
with open(output_json_path, "w") as f:
@@ -157,7 +165,7 @@ def bench(f, file_like=False, average_over=50, warmup=2, **f_kwargs):
157165
# so the performance benefits are realized.
158166

159167

160-
def decode_frames(video_path, seek_mode = "exact", custom_frame_mappings = None):
168+
def decode_frames(video_path, seek_mode="exact", custom_frame_mappings=None):
161169
decoder = VideoDecoder(
162170
source=video_path,
163171
seek_mode=seek_mode,

examples/decoding/parallel_decoding.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
# require efficient processing. You can ignore that part and jump right below to
3232
# :ref:`start_parallel_decoding`.
3333

34-
from typing import List
3534
import torch
3635
import requests
3736
import tempfile
@@ -74,7 +73,7 @@ def report_stats(times, unit="s"):
7473
return med
7574

7675

77-
def split_indices(indices: List[int], num_chunks: int) -> List[List[int]]:
76+
def split_indices(indices: list[int], num_chunks: int) -> list[list[int]]:
7877
"""Split a list of indices into approximately equal chunks."""
7978
chunk_size = len(indices) // num_chunks
8079
chunks = []
@@ -155,7 +154,8 @@ def generate_long_video(temp_dir: str):
155154
# Let's start with a sequential approach as our baseline. This processes
156155
# frames one by one without any parallelization.
157156

158-
def decode_sequentially(indices: List[int], video_path=long_video_path):
157+
158+
def decode_sequentially(indices: list[int], video_path=long_video_path):
159159
"""Decode frames sequentially using a single decoder instance."""
160160
decoder = VideoDecoder(video_path, seek_mode="approximate")
161161
return decoder.get_frames_at(indices)
@@ -173,8 +173,9 @@ def decode_sequentially(indices: List[int], video_path=long_video_path):
173173
# via the ``num_ffmpeg_threads`` parameter. This approach uses multiple
174174
# threads within FFmpeg itself to accelerate decoding operations.
175175

176+
176177
def decode_with_ffmpeg_parallelism(
177-
indices: List[int],
178+
indices: list[int],
178179
num_threads: int,
179180
video_path=long_video_path
180181
):
@@ -197,10 +198,11 @@ def decode_with_ffmpeg_parallelism(
197198
#
198199
# Process-based parallelism distributes work across multiple Python processes.
199200

201+
200202
def decode_with_multiprocessing(
201-
indices: List[int],
203+
indices: list[int],
202204
num_processes: int,
203-
video_path=long_video_path
205+
video_path=long_video_path,
204206
):
205207
"""Decode frames using multiple processes with joblib."""
206208
chunks = split_indices(indices, num_chunks=num_processes)
@@ -226,8 +228,9 @@ def decode_with_multiprocessing(
226228
# Thread-based parallelism uses multiple threads within a single process.
227229
# TorchCodec releases the GIL, so this can be very effective.
228230

231+
229232
def decode_with_multithreading(
230-
indices: List[int],
233+
indices: list[int],
231234
num_threads: int,
232235
video_path=long_video_path
233236
):

examples/decoding/sampling.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
# plotting utility. You can ignore that part and jump right below to
2020
# :ref:`sampling_tuto_start`.
2121

22-
from typing import Optional
2322
import torch
2423
import requests
2524

@@ -34,7 +33,7 @@
3433
raw_video_bytes = response.content
3534

3635

37-
def plot(frames: torch.Tensor, title : Optional[str] = None):
36+
def plot(frames: torch.Tensor, title: str | None = None):
3837
try:
3938
from torchvision.utils import make_grid
4039
from torchvision.transforms.v2.functional import to_pil_image

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "torchcodec"
33
description = "A video decoder for PyTorch"
44
readme = "README.md"
5-
requires-python = ">=3.8"
5+
requires-python = ">=3.10"
66
license-files = ["LICENSE"]
77
authors = [
88
{ name = "PyTorch Team", email = "[email protected]" },
@@ -32,7 +32,7 @@ dev = [
3232
first_party_detection = false
3333

3434
[tool.black]
35-
target-version = ["py38"]
35+
target-version = ["py310"]
3636

3737
[tool.ufmt]
3838

src/torchcodec/_core/_metadata.py

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
78
import dataclasses
89
import json
910
import pathlib
1011
from dataclasses import dataclass
1112
from fractions import Fraction
12-
from typing import List, Optional, Union
1313

1414
import torch
1515

@@ -25,29 +25,29 @@
2525

2626
@dataclass
2727
class StreamMetadata:
28-
duration_seconds_from_header: Optional[float]
28+
duration_seconds_from_header: float | None
2929
"""Duration of the stream, in seconds, obtained from the header (float or
3030
None). This could be inaccurate."""
31-
begin_stream_seconds_from_header: Optional[float]
31+
begin_stream_seconds_from_header: float | None
3232
"""Beginning of the stream, in seconds, obtained from the header (float or
3333
None). Usually, this is equal to 0."""
34-
bit_rate: Optional[float]
34+
bit_rate: float | None
3535
"""Bit rate of the stream, in seconds (float or None)."""
36-
codec: Optional[str]
36+
codec: str | None
3737
"""Codec (str or None)."""
3838
stream_index: int
3939
"""Index of the stream that this metadata refers to (int)."""
4040

4141
# Computed fields (computed in C++ with fallback logic)
42-
duration_seconds: Optional[float]
42+
duration_seconds: float | None
4343
"""Duration of the stream in seconds. We try to calculate the duration
4444
from the actual frames if a :term:`scan` was performed. Otherwise we
4545
fall back to ``duration_seconds_from_header``. If that value is also None,
4646
we instead calculate the duration from ``num_frames_from_header`` and
4747
``average_fps_from_header``. If all of those are unavailable, we fall back
4848
to the container-level ``duration_seconds_from_header``.
4949
"""
50-
begin_stream_seconds: Optional[float]
50+
begin_stream_seconds: float | None
5151
"""Beginning of the stream, in seconds (float). Conceptually, this
5252
corresponds to the first frame's :term:`pts`. If a :term:`scan` was performed
5353
and ``begin_stream_seconds_from_content`` is not None, then it is returned.
@@ -65,12 +65,12 @@ def __repr__(self):
6565
class VideoStreamMetadata(StreamMetadata):
6666
"""Metadata of a single video stream."""
6767

68-
begin_stream_seconds_from_content: Optional[float]
68+
begin_stream_seconds_from_content: float | None
6969
"""Beginning of the stream, in seconds (float or None).
7070
Conceptually, this corresponds to the first frame's :term:`pts`. It is only
7171
computed when a :term:`scan` is done as min(frame.pts) across all frames in
7272
the stream. Usually, this is equal to 0."""
73-
end_stream_seconds_from_content: Optional[float]
73+
end_stream_seconds_from_content: float | None
7474
"""End of the stream, in seconds (float or None).
7575
Conceptually, this corresponds to last_frame.pts + last_frame.duration. It
7676
is only computed when a :term:`scan` is done as max(frame.pts +
@@ -81,42 +81,42 @@ class VideoStreamMetadata(StreamMetadata):
8181
simply indexing the :class:`~torchcodec.decoders.VideoDecoder` object with
8282
``[-1]``.
8383
"""
84-
width: Optional[int]
84+
width: int | None
8585
"""Width of the frames (int or None)."""
86-
height: Optional[int]
86+
height: int | None
8787
"""Height of the frames (int or None)."""
88-
num_frames_from_header: Optional[int]
88+
num_frames_from_header: int | None
8989
"""Number of frames, from the stream's metadata. This is potentially
9090
inaccurate. We recommend using the ``num_frames`` attribute instead.
9191
(int or None)."""
92-
num_frames_from_content: Optional[int]
92+
num_frames_from_content: int | None
9393
"""Number of frames computed by TorchCodec by scanning the stream's
9494
content (the scan doesn't involve decoding). This is more accurate
9595
than ``num_frames_from_header``. We recommend using the
9696
``num_frames`` attribute instead. (int or None)."""
97-
average_fps_from_header: Optional[float]
97+
average_fps_from_header: float | None
9898
"""Averate fps of the stream, obtained from the header (float or None).
9999
We recommend using the ``average_fps`` attribute instead."""
100-
pixel_aspect_ratio: Optional[Fraction]
100+
pixel_aspect_ratio: Fraction | None
101101
"""Pixel Aspect Ratio (PAR), also known as Sample Aspect Ratio
102102
(SAR --- not to be confused with Storage Aspect Ratio, also SAR),
103103
is the ratio between the width and height of each pixel
104104
(``fractions.Fraction`` or None)."""
105105

106106
# Computed fields (computed in C++ with fallback logic)
107-
end_stream_seconds: Optional[float]
107+
end_stream_seconds: float | None
108108
"""End of the stream, in seconds (float or None).
109109
Conceptually, this corresponds to last_frame.pts + last_frame.duration.
110110
If :term:`scan` was performed and``end_stream_seconds_from_content`` is not None, then that value is
111111
returned. Otherwise, returns ``duration_seconds``.
112112
"""
113-
num_frames: Optional[int]
113+
num_frames: int | None
114114
"""Number of frames in the stream (int or None).
115115
This corresponds to ``num_frames_from_content`` if a :term:`scan` was made,
116116
otherwise it corresponds to ``num_frames_from_header``. If that value is also
117117
None, the number of frames is calculated from the duration and the average fps.
118118
"""
119-
average_fps: Optional[float]
119+
average_fps: float | None
120120
"""Average fps of the stream. If a :term:`scan` was perfomed, this is
121121
computed from the number of frames and the duration of the stream.
122122
Otherwise we fall back to ``average_fps_from_header``.
@@ -130,11 +130,11 @@ def __repr__(self):
130130
class AudioStreamMetadata(StreamMetadata):
131131
"""Metadata of a single audio stream."""
132132

133-
sample_rate: Optional[int]
133+
sample_rate: int | None
134134
"""The original sample rate."""
135-
num_channels: Optional[int]
135+
num_channels: int | None
136136
"""The number of channels (1 for mono, 2 for stereo, etc.)"""
137-
sample_format: Optional[str]
137+
sample_format: str | None
138138
"""The original sample format, as described by FFmpeg. E.g. 'fltp', 's32', etc."""
139139

140140
def __repr__(self):
@@ -143,19 +143,19 @@ def __repr__(self):
143143

144144
@dataclass
145145
class ContainerMetadata:
146-
duration_seconds_from_header: Optional[float]
147-
bit_rate_from_header: Optional[float]
148-
best_video_stream_index: Optional[int]
149-
best_audio_stream_index: Optional[int]
146+
duration_seconds_from_header: float | None
147+
bit_rate_from_header: float | None
148+
best_video_stream_index: int | None
149+
best_audio_stream_index: int | None
150150

151-
streams: List[StreamMetadata]
151+
streams: list[StreamMetadata]
152152

153153
@property
154-
def duration_seconds(self) -> Optional[float]:
154+
def duration_seconds(self) -> float | None:
155155
raise NotImplementedError("Decide on logic and implement this!")
156156

157157
@property
158-
def bit_rate(self) -> Optional[float]:
158+
def bit_rate(self) -> float | None:
159159
raise NotImplementedError("Decide on logic and implement this!")
160160

161161
@property
@@ -195,7 +195,7 @@ def get_container_metadata(decoder: torch.Tensor) -> ContainerMetadata:
195195
"""
196196

197197
container_dict = json.loads(_get_container_json_metadata(decoder))
198-
streams_metadata: List[StreamMetadata] = []
198+
streams_metadata: list[StreamMetadata] = []
199199
for stream_index in range(container_dict["numStreams"]):
200200
stream_dict = json.loads(_get_stream_json_metadata(decoder, stream_index))
201201
common_meta = dict(
@@ -255,7 +255,7 @@ def get_container_metadata(decoder: torch.Tensor) -> ContainerMetadata:
255255

256256

257257
def get_container_metadata_from_header(
258-
filename: Union[str, pathlib.Path]
258+
filename: str | pathlib.Path,
259259
) -> ContainerMetadata:
260260
return get_container_metadata(
261261
create_from_file(str(filename), seek_mode="approximate")

0 commit comments

Comments
 (0)