From c79c3195560659e2e15c8d6ce778c7abd6a9e32f Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus Date: Fri, 5 Apr 2024 15:53:09 +0200 Subject: [PATCH 1/7] Fix type errors and maybe a bug in to_annotation --- pyannote/core/timeline.py | 88 +++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/pyannote/core/timeline.py b/pyannote/core/timeline.py index 1af9151..6a88281 100755 --- a/pyannote/core/timeline.py +++ b/pyannote/core/timeline.py @@ -92,6 +92,8 @@ from typing import (Optional, Iterable, List, Union, Callable, TextIO, Tuple, TYPE_CHECKING, Iterator, Dict, Text) +# sortedcontainers does not support type hinting +# introduces some type: ignore comments below. from sortedcontainers import SortedList from . import PYANNOTE_SEGMENT @@ -104,7 +106,7 @@ # https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports if TYPE_CHECKING: from .annotation import Annotation - import pandas as pd + import pandas as pd # type: ignore # ===================================================================== @@ -141,13 +143,13 @@ def from_df(cls, df: 'pd.DataFrame', uri: Optional[str] = None) -> 'Timeline': def __init__(self, segments: Optional[Iterable[Segment]] = None, - uri: str = None): + uri: Optional[str] = None): if segments is None: segments = () # set of segments (used for checking inclusion) # Store only non-empty Segments. - segments_set = set([segment for segment in segments if segment]) + segments_set = set(segment for segment in segments if segment) self.segments_set_ = segments_set @@ -159,7 +161,7 @@ def __init__(self, self.segments_boundaries_ = SortedList(boundaries) # path to (or any identifier of) segmented resource - self.uri: str = uri + self.uri: Optional[str] = uri def __len__(self): """Number of segments @@ -200,7 +202,7 @@ def __getitem__(self, k: int) -> Segment: >>> first_segment = timeline[0] >>> penultimate_segment = timeline[-2] """ - return self.segments_list_[k] + return self.segments_list_[k] # type: ignore def __eq__(self, other: 'Timeline'): """Equality @@ -320,7 +322,7 @@ def discard(self, segment: Segment) -> 'Timeline': def __ior__(self, timeline: 'Timeline') -> 'Timeline': return self.update(timeline) - def update(self, timeline: Segment) -> 'Timeline': + def update(self, timeline: 'Timeline') -> 'Timeline': """Add every segments of an existing timeline (in place) Parameters @@ -465,6 +467,20 @@ def crop_iter(self, else: yield mapped_to + def crop_iter_no_mapping(self, + support: Support, + mode: CropMode = 'intersection') \ + -> Iterator[Segment]: + """Typed version of crop_iter without mapping""" + return self.crop_iter(support=support, mode=mode, returns_mapping=False) # type: ignore + + def crop_iter_with_mapping(self, + support: Support, + mode: CropMode = 'intersection') \ + -> Iterator[Tuple[Segment, Segment]]: + """Typed version of crop_iter with mapping""" + return self.crop_iter(support=support, mode=mode, returns_mapping=True) # type: ignore + def crop(self, support: Support, mode: CropMode = 'intersection', @@ -516,16 +532,29 @@ def crop(self, if mode == 'intersection' and returns_mapping: segments, mapping = [], {} - for segment, mapped_to in self.crop_iter(support, - mode='intersection', - returns_mapping=True): + for segment, mapped_to in self.crop_iter_with_mapping(support, + mode='intersection'): segments.append(mapped_to) mapping[mapped_to] = mapping.get(mapped_to, list()) + [segment] return Timeline(segments=segments, uri=self.uri), mapping - return Timeline(segments=self.crop_iter(support, mode=mode), + return Timeline(segments=self.crop_iter_no_mapping(support, mode=mode), uri=self.uri) + def crop_with_mapping(self, + support: Support, + mode: CropMode = 'intersection') \ + -> Tuple['Timeline', Dict[Segment, List[Segment]]]: + """Typed version of crop with mapping""" + return self.crop(support=support, mode=mode, returns_mapping=True) # type: ignore + + def crop_no_mapping(self, + support: Support, + mode: CropMode = 'intersection') \ + -> 'Timeline': + """Typed version of crop without mapping""" + return self.crop(support=support, mode=mode, returns_mapping=False) # type: ignore + def overlapping(self, t: float) -> List[Segment]: """Get list of segments overlapping `t` @@ -622,7 +651,7 @@ def extrude(self, mode = "strict" elif mode == "strict": mode = "loose" - return self.crop(truncating_support, mode=mode) + return self.crop_no_mapping(truncating_support, mode=mode) def __str__(self): """Human-readable representation @@ -705,7 +734,7 @@ def empty(self) -> 'Timeline': def covers(self, other: 'Timeline') -> bool: """Check whether other timeline is fully covered by the timeline - + Parameter --------- other : Timeline @@ -718,16 +747,16 @@ def covers(self, other: 'Timeline') -> bool: one segment of "other" is not fully covered by timeline """ - # compute gaps within "other" extent - # this is where we should look for possible faulty segments + # compute gaps within "other" extent + # this is where we should look for possible faulty segments gaps = self.gaps(support=other.extent()) - # if at least one gap intersects with a segment from "other", + # if at least one gap intersects with a segment from "other", # "self" does not cover "other" entirely --> return False for _ in gaps.co_iter(other): return False - # if no gap intersects with a segment from "other", + # if no gap intersects with a segment from "other", # "self" covers "other" entirely --> return True return True @@ -790,8 +819,8 @@ def extent(self) -> Segment: """ if self.segments_set_: segments_boundaries_ = self.segments_boundaries_ - start = segments_boundaries_[0] - end = segments_boundaries_[-1] + start: float = segments_boundaries_[0] # type: ignore + end: float = segments_boundaries_[-1] # type: ignore return Segment(start=start, end=end) return Segment(start=0.0, end=0.0) @@ -818,7 +847,7 @@ def support_iter(self, collar: float = 0.0) -> Iterator[Segment]: # Initialize new support segment # as very first segment of the timeline - new_segment = self.segments_list_[0] + new_segment: Segment = self.segments_list_[0] # type: ignore for segment in self: @@ -918,7 +947,7 @@ def gaps_iter(self, support: Optional[Support] = None) -> Iterator[Segment]: end = support.start # support on the intersection of timeline and provided segment - for segment in self.crop(support, mode='intersection').support(): + for segment in self.crop_no_mapping(support, mode='intersection').support(): # add gap between each pair of consecutive segments # if there is no gap, segment is empty, therefore not added @@ -1015,8 +1044,6 @@ def segmentation(self) -> 'Timeline': # becomes # |-|--|-| |-|---|--| |--|----|--| - # start with an empty copy - timeline = Timeline(uri=self.uri) if len(timestamps) == 0: return Timeline(uri=self.uri) @@ -1034,7 +1061,7 @@ def segmentation(self) -> 'Timeline': return Timeline(segments=segments, uri=self.uri) def to_annotation(self, - generator: Union[str, Iterable[Label], None, None] = 'string', + generator: Union[str, Iterable[Label], None] = 'string', modality: Optional[str] = None) \ -> 'Annotation': """Turn timeline into an annotation @@ -1056,15 +1083,22 @@ def to_annotation(self, from .annotation import Annotation annotation = Annotation(uri=self.uri, modality=modality) - if generator == 'string': + if generator == 'string' or generator is None: from .utils.generators import string_generator - generator = string_generator() + generator_ = string_generator() elif generator == 'int': from .utils.generators import int_generator - generator = int_generator() + generator_ = int_generator() + elif isinstance(generator, Iterable): + generator_ = iter(generator) + else: + msg = ("`generator` must be one of 'string', 'int', or an iterable " + "(got {generator}).") + raise ValueError(msg.format(generator=generator)) + for segment in self: - annotation[segment] = next(generator) + annotation[segment] = next(generator_) return annotation From 15fe7a8e49ad4b1af9a58f6702e4883a10ec259c Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus Date: Fri, 5 Apr 2024 16:07:03 +0200 Subject: [PATCH 2/7] Fix type hinting in pyannote.core.utils.types.py --- pyannote/core/utils/types.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyannote/core/utils/types.py b/pyannote/core/utils/types.py index 0105a16..2751bec 100644 --- a/pyannote/core/utils/types.py +++ b/pyannote/core/utils/types.py @@ -1,7 +1,14 @@ -from typing import Hashable, Union, Tuple, Iterator +from typing import Hashable, Union, Tuple, Iterator, TYPE_CHECKING from typing_extensions import Literal +if TYPE_CHECKING: + from pyannote.core.segment import Segment + from pyannote.core.timeline import Timeline + from pyannote.core.feature import SlidingWindowFeature + from pyannote.core.annotation import Annotation + + Label = Hashable Support = Union['Segment', 'Timeline'] LabelGeneratorMode = Literal['int', 'string'] From 5d9c59111c9705c25939e8d218eab37df0a81ad0 Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus Date: Fri, 5 Apr 2024 16:07:12 +0200 Subject: [PATCH 3/7] Fix type hinting in pyannote.core.feature.py --- pyannote/core/feature.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyannote/core/feature.py b/pyannote/core/feature.py index ca95142..b39f035 100755 --- a/pyannote/core/feature.py +++ b/pyannote/core/feature.py @@ -36,11 +36,11 @@ """ import numbers import warnings -from typing import Tuple, Optional, Union, Iterator, List, Text +from typing import Tuple, Optional, Union, Iterator, List import numpy as np -from pyannote.core.utils.types import Alignment +from pyannote.core.utils.types import Alignment, Label from .segment import Segment from .segment import SlidingWindow from .timeline import Timeline @@ -58,7 +58,7 @@ class SlidingWindowFeature(np.lib.mixins.NDArrayOperatorsMixin): """ def __init__( - self, data: np.ndarray, sliding_window: SlidingWindow, labels: List[Text] = None + self, data: np.ndarray, sliding_window: SlidingWindow, labels: Optional[List[Label]] = None ): self.sliding_window: SlidingWindow = sliding_window self.data = data @@ -106,7 +106,7 @@ def __next__(self) -> Tuple[Segment, np.ndarray]: self.__i += 1 try: return self.sliding_window[self.__i], self.data[self.__i] - except IndexError as e: + except IndexError: raise StopIteration() def next(self): From cc5b190c7a88ad7f56578a5c3b0ba53aa9216997 Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus Date: Fri, 5 Apr 2024 16:07:53 +0200 Subject: [PATCH 4/7] Fix type hinting and add named tuples --- .gitignore | 3 +- pyannote/core/annotation.py | 81 ++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index bce3533..2a7afe6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ MANIFEST .Python env/ +venv/ bin/ build/ develop-eggs/ @@ -61,4 +62,4 @@ doc/.ipynb_checkpoints # PyCharm .idea/ -.mypy_cache/ \ No newline at end of file +.mypy_cache/ diff --git a/pyannote/core/annotation.py b/pyannote/core/annotation.py index fc2d17a..5176cfb 100755 --- a/pyannote/core/annotation.py +++ b/pyannote/core/annotation.py @@ -122,6 +122,7 @@ Iterator, Text, TYPE_CHECKING, + NamedTuple, ) import numpy as np @@ -139,7 +140,17 @@ from .utils.types import Label, Key, Support, LabelGenerator, TrackName, CropMode if TYPE_CHECKING: - import pandas as pd + import pandas as pd # type: ignore + + +class SegmentTrack(NamedTuple): + segment: Segment + track: TrackName + +class SegmentTrackLabel(NamedTuple): + segment: Segment + track: TrackName + label: Label class Annotation: @@ -187,7 +198,7 @@ def __init__(self, uri: Optional[str] = None, modality: Optional[str] = None): self._labelNeedsUpdate: Dict[Label, bool] = {} # timeline meant to store all annotated segments - self._timeline: Timeline = None + self._timeline: Optional[Timeline] = None self._timelineNeedsUpdate: bool = True @property @@ -213,7 +224,7 @@ def _updateLabels(self): # accumulate segments for updated labels _segments = {label: [] for label in update} - for segment, track, label in self.itertracks(yield_label=True): + for segment, track, label in self.itertracks_with_labels(): if label in update: _segments[label].append(segment) @@ -261,9 +272,13 @@ def itersegments(self): def itertracks( self, yield_label: bool = False - ) -> Iterator[Union[Tuple[Segment, TrackName], Tuple[Segment, TrackName, Label]]]: + ) -> Iterator[Union[SegmentTrack, SegmentTrackLabel]]: """Iterate over tracks (in chronological order) + Typed version of :func:`itertracks`: + - :func:`itertracks_without_labels` yields (segment, track) tuples (SegmentTrack) + - :func:`itertracks_with_labels` yields (segment, track, label) tuples (SegmentTrackLabel) + Parameters ---------- yield_label : bool, optional @@ -277,7 +292,7 @@ def itertracks( >>> for segment, track in annotation.itertracks(): ... # do something with the track - >>> for segment, track, label in annotation.itertracks(yield_label=True): + >>> for segment, track, label in annotation.itertracks_with_labels(): ... # do something with the track and its label """ @@ -286,9 +301,17 @@ def itertracks( tracks.items(), key=lambda tl: (str(tl[0]), str(tl[1])) ): if yield_label: - yield segment, track, lbl + yield SegmentTrackLabel(segment, track, lbl) else: - yield segment, track + yield SegmentTrack(segment, track) + + def itertracks_with_labels(self) -> Iterator[SegmentTrackLabel]: + """Typed version of :func:`itertracks`(yield_label=True)""" + return self.itertracks_with_labels() # type: ignore + + def itertracks_without_labels(self) -> Iterator[SegmentTrack]: + """Typed version of :func:`itertracks`(yield_label=False)""" + return self.itertracks(yield_label=False) # type: ignore def _updateTimeline(self): self._timeline = Timeline(segments=self._tracks, uri=self.uri) @@ -317,9 +340,14 @@ def get_timeline(self, copy: bool = True) -> Timeline: """ if self._timelineNeedsUpdate: self._updateTimeline() + + timeline_ = self._timeline + if timeline_ is None: + timeline_ = Timeline(uri=self.uri) + if copy: - return self._timeline.copy() - return self._timeline + return timeline_.copy() + return timeline_ def __eq__(self, other: "Annotation"): """Equality @@ -330,14 +358,14 @@ def __eq__(self, other: "Annotation"): labels are equal. """ pairOfTracks = itertools.zip_longest( - self.itertracks(yield_label=True), other.itertracks(yield_label=True) + self.itertracks_with_labels(), other.itertracks_with_labels() ) return all(t1 == t2 for t1, t2 in pairOfTracks) def __ne__(self, other: "Annotation"): """Inequality""" pairOfTracks = itertools.zip_longest( - self.itertracks(yield_label=True), other.itertracks(yield_label=True) + self.itertracks_with_labels(), other.itertracks_with_labels() ) return any(t1 != t2 for t1, t2 in pairOfTracks) @@ -376,7 +404,7 @@ def _iter_rttm(self) -> Iterator[Text]: f'containing spaces (got: "{uri}").' ) raise ValueError(msg) - for segment, _, label in self.itertracks(yield_label=True): + for segment, _, label in self.itertracks_with_labels(): if isinstance(label, Text) and " " in label: msg = ( f"Space-separated RTTM file format does not allow labels " @@ -421,7 +449,7 @@ def _iter_lab(self) -> Iterator[Text]: iterator: Iterator[str] An iterator over LAB text lines """ - for segment, _, label in self.itertracks(yield_label=True): + for segment, _, label in self.itertracks_with_labels(): if isinstance(label, Text) and " " in label: msg = ( f"Space-separated LAB file format does not allow labels " @@ -556,6 +584,9 @@ def crop(self, support: Support, mode: CropMode = "intersection") -> "Annotation else: raise NotImplementedError("unsupported mode: '%s'" % mode) + else: + raise TypeError("unsupported support type: '%s'" % type(support)) + def extrude( self, removed: Support, mode: CropMode = "intersection" ) -> "Annotation": @@ -775,7 +806,7 @@ def __str__(self): """Human-friendly representation""" # TODO: use pandas.DataFrame return "\n".join( - ["%s %s %s" % (s, t, l) for s, t, l in self.itertracks(yield_label=True)] + ["%s %s %s" % (s, t, l) for s, t, l in self.itertracks_with_labels()] ) def __delitem__(self, key: Key): @@ -1020,7 +1051,7 @@ def update(self, annotation: "Annotation", copy: bool = False) -> "Annotation": result = self.copy() if copy else self # TODO speed things up by working directly with annotation internals - for segment, track, label in annotation.itertracks(yield_label=True): + for segment, track, label in annotation.itertracks_with_labels(): result[segment, track] = label return result @@ -1178,7 +1209,7 @@ def argmax(self, support: Optional[Support] = None) -> Optional[Label]: key=lambda x: x[1], )[0] - def rename_tracks(self, generator: LabelGenerator = "string") -> "Annotation": + def rename_tracks(self, generator: Union[LabelGenerator, Iterable[str], Iterable[int]] = "string") -> "Annotation": """Rename all tracks Parameters @@ -1215,13 +1246,17 @@ def rename_tracks(self, generator: LabelGenerator = "string") -> "Annotation": renamed = self.__class__(uri=self.uri, modality=self.modality) if generator == "string": - generator = string_generator() + generator_ = string_generator() elif generator == "int": - generator = int_generator() + generator_ = int_generator() + elif isinstance(generator, Iterable): + generator_ = iter(generator) + else: + raise ValueError("generator must be 'string', 'int', or iterable") # TODO speed things up by working directly with annotation internals - for s, _, label in self.itertracks(yield_label=True): - renamed[s, next(generator)] = label + for s, _, label in self.itertracks_with_labels(): + renamed[s, next(generator_)] = label return renamed def rename_labels( @@ -1303,7 +1338,7 @@ def relabel_tracks(self, generator: LabelGenerator = "string") -> "Annotation": generator = int_generator() relabeled = self.empty() - for s, t, _ in self.itertracks(yield_label=True): + for s, t, _ in self.itertracks_with_labels(): relabeled[s, t] = next(generator) return relabeled @@ -1439,11 +1474,11 @@ def discretize( duration: Optional[float] = None, ): """Discretize - + Parameters ---------- support : Segment, optional - Part of annotation to discretize. + Part of annotation to discretize. Defaults to annotation full extent. resolution : float or SlidingWindow, optional Defaults to 10ms frames. From f81a75a8459c4b3ba32600808fbc1c41e613d9a0 Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus <69142583+simonottenhauskenbun@users.noreply.github.com> Date: Wed, 17 Apr 2024 08:27:33 +0200 Subject: [PATCH 5/7] Fix infinite recursion Co-authored-by: Evan Finken --- pyannote/core/annotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyannote/core/annotation.py b/pyannote/core/annotation.py index 5176cfb..7a9c382 100755 --- a/pyannote/core/annotation.py +++ b/pyannote/core/annotation.py @@ -307,7 +307,7 @@ def itertracks( def itertracks_with_labels(self) -> Iterator[SegmentTrackLabel]: """Typed version of :func:`itertracks`(yield_label=True)""" - return self.itertracks_with_labels() # type: ignore + return self.itertracks(yield_label=True) # type: ignore def itertracks_without_labels(self) -> Iterator[SegmentTrack]: """Typed version of :func:`itertracks`(yield_label=False)""" From 230cf3b70b82ef795763f126e0348d3ace5db296 Mon Sep 17 00:00:00 2001 From: Simon Ottenhaus Date: Wed, 17 Apr 2024 08:37:51 +0200 Subject: [PATCH 6/7] use overload bool-literal typing --- pyannote/core/annotation.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/pyannote/core/annotation.py b/pyannote/core/annotation.py index 7a9c382..2383140 100755 --- a/pyannote/core/annotation.py +++ b/pyannote/core/annotation.py @@ -111,6 +111,7 @@ from collections import defaultdict from typing import ( Hashable, + Literal, Optional, Dict, Union, @@ -123,6 +124,7 @@ Text, TYPE_CHECKING, NamedTuple, + overload, ) import numpy as np @@ -224,7 +226,7 @@ def _updateLabels(self): # accumulate segments for updated labels _segments = {label: [] for label in update} - for segment, track, label in self.itertracks_with_labels(): + for segment, track, label in self.itertracks(yield_label=True): if label in update: _segments[label].append(segment) @@ -270,6 +272,13 @@ def itersegments(self): """ return iter(self._tracks) + @overload + def itertracks(self, yield_label: Literal[False] = ...) -> Iterator[SegmentTrack]: ... + @overload + def itertracks(self, yield_label: Literal[True]) -> Iterator[SegmentTrackLabel]: ... + @overload + def itertracks(self, yield_label: bool) -> Iterator[Union[SegmentTrack, SegmentTrackLabel]]: ... + def itertracks( self, yield_label: bool = False ) -> Iterator[Union[SegmentTrack, SegmentTrackLabel]]: @@ -292,7 +301,7 @@ def itertracks( >>> for segment, track in annotation.itertracks(): ... # do something with the track - >>> for segment, track, label in annotation.itertracks_with_labels(): + >>> for segment, track, label in annotation.itertracks(yield_label=True): ... # do something with the track and its label """ @@ -307,11 +316,11 @@ def itertracks( def itertracks_with_labels(self) -> Iterator[SegmentTrackLabel]: """Typed version of :func:`itertracks`(yield_label=True)""" - return self.itertracks(yield_label=True) # type: ignore + return self.itertracks(yield_label=True) def itertracks_without_labels(self) -> Iterator[SegmentTrack]: """Typed version of :func:`itertracks`(yield_label=False)""" - return self.itertracks(yield_label=False) # type: ignore + return self.itertracks(yield_label=False) def _updateTimeline(self): self._timeline = Timeline(segments=self._tracks, uri=self.uri) @@ -358,14 +367,14 @@ def __eq__(self, other: "Annotation"): labels are equal. """ pairOfTracks = itertools.zip_longest( - self.itertracks_with_labels(), other.itertracks_with_labels() + self.itertracks(yield_label=True), other.itertracks(yield_label=True) ) return all(t1 == t2 for t1, t2 in pairOfTracks) def __ne__(self, other: "Annotation"): """Inequality""" pairOfTracks = itertools.zip_longest( - self.itertracks_with_labels(), other.itertracks_with_labels() + self.itertracks(yield_label=True), other.itertracks(yield_label=True) ) return any(t1 != t2 for t1, t2 in pairOfTracks) @@ -404,7 +413,7 @@ def _iter_rttm(self) -> Iterator[Text]: f'containing spaces (got: "{uri}").' ) raise ValueError(msg) - for segment, _, label in self.itertracks_with_labels(): + for segment, _, label in self.itertracks(yield_label=True): if isinstance(label, Text) and " " in label: msg = ( f"Space-separated RTTM file format does not allow labels " @@ -449,7 +458,7 @@ def _iter_lab(self) -> Iterator[Text]: iterator: Iterator[str] An iterator over LAB text lines """ - for segment, _, label in self.itertracks_with_labels(): + for segment, _, label in self.itertracks(yield_label=True): if isinstance(label, Text) and " " in label: msg = ( f"Space-separated LAB file format does not allow labels " @@ -806,7 +815,7 @@ def __str__(self): """Human-friendly representation""" # TODO: use pandas.DataFrame return "\n".join( - ["%s %s %s" % (s, t, l) for s, t, l in self.itertracks_with_labels()] + ["%s %s %s" % (s, t, l) for s, t, l in self.itertracks(yield_label=True)] ) def __delitem__(self, key: Key): @@ -1051,7 +1060,7 @@ def update(self, annotation: "Annotation", copy: bool = False) -> "Annotation": result = self.copy() if copy else self # TODO speed things up by working directly with annotation internals - for segment, track, label in annotation.itertracks_with_labels(): + for segment, track, label in annotation.itertracks(yield_label=True): result[segment, track] = label return result @@ -1255,7 +1264,7 @@ def rename_tracks(self, generator: Union[LabelGenerator, Iterable[str], Iterable raise ValueError("generator must be 'string', 'int', or iterable") # TODO speed things up by working directly with annotation internals - for s, _, label in self.itertracks_with_labels(): + for s, _, label in self.itertracks(yield_label=True): renamed[s, next(generator_)] = label return renamed @@ -1338,7 +1347,7 @@ def relabel_tracks(self, generator: LabelGenerator = "string") -> "Annotation": generator = int_generator() relabeled = self.empty() - for s, t, _ in self.itertracks_with_labels(): + for s, t, _ in self.itertracks(yield_label=True): relabeled[s, t] = next(generator) return relabeled From 9ef14113536e1d5fdb316a7e5ce94c3a39c8fca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20BREDIN?= Date: Sun, 12 Jan 2025 21:16:05 +0100 Subject: [PATCH 7/7] Update annotation.py --- pyannote/core/annotation.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pyannote/core/annotation.py b/pyannote/core/annotation.py index 2383140..84fec6f 100755 --- a/pyannote/core/annotation.py +++ b/pyannote/core/annotation.py @@ -284,10 +284,6 @@ def itertracks( ) -> Iterator[Union[SegmentTrack, SegmentTrackLabel]]: """Iterate over tracks (in chronological order) - Typed version of :func:`itertracks`: - - :func:`itertracks_without_labels` yields (segment, track) tuples (SegmentTrack) - - :func:`itertracks_with_labels` yields (segment, track, label) tuples (SegmentTrackLabel) - Parameters ---------- yield_label : bool, optional @@ -314,14 +310,6 @@ def itertracks( else: yield SegmentTrack(segment, track) - def itertracks_with_labels(self) -> Iterator[SegmentTrackLabel]: - """Typed version of :func:`itertracks`(yield_label=True)""" - return self.itertracks(yield_label=True) - - def itertracks_without_labels(self) -> Iterator[SegmentTrack]: - """Typed version of :func:`itertracks`(yield_label=False)""" - return self.itertracks(yield_label=False) - def _updateTimeline(self): self._timeline = Timeline(segments=self._tracks, uri=self.uri) self._timelineNeedsUpdate = False