Skip to content

Commit

Permalink
feat: add type hinting and python3-ize code base
Browse files Browse the repository at this point in the history
  • Loading branch information
hadware authored and hbredin committed Dec 13, 2019
1 parent 8ae09e4 commit 1380273
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 282 deletions.
136 changes: 77 additions & 59 deletions pyannote/core/annotation.py

Large diffs are not rendered by default.

63 changes: 43 additions & 20 deletions pyannote/core/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,19 @@
See :class:`pyannote.core.SlidingWindowFeature` for the complete reference.
"""
import numbers
import warnings
from typing import Tuple, Optional, Union, Iterator

import numpy as np
import numbers

from pyannote.core.utils.types import CropMode
from .segment import Segment
from .segment import SlidingWindow
from .timeline import Timeline


class SlidingWindowFeature(np.lib.mixins.NDArrayOperatorsMixin):

"""Periodic feature vectors
Parameters
Expand All @@ -55,35 +58,48 @@ class SlidingWindowFeature(np.lib.mixins.NDArrayOperatorsMixin):
"""

def __init__(self, data, sliding_window):
super(SlidingWindowFeature, self).__init__()
self.sliding_window = sliding_window
def __init__(self, data: np.ndarray, sliding_window: SlidingWindow):
self.sliding_window: SlidingWindow = sliding_window
self.data = data
self.__i = -1
self.__i: int = -1

def __len__(self):
"""Number of feature vectors"""
return self.data.shape[0]

@property
def extent(self):
return self.sliding_window.range_to_segment(0, len(self))

@property
def dimension(self):
"""Dimension of feature vectors"""
return self.data.shape[1]

def getNumber(self):
"""Number of feature vectors"""
warnings.warn("This is deprecated in favor of `__len__`",
DeprecationWarning)
return self.data.shape[0]

def getDimension(self):
"""Dimension of feature vectors"""
return self.data.shape[1]
warnings.warn("This is deprecated in favor of `dimension` property",
DeprecationWarning)
return self.dimension

def getExtent(self):
return self.sliding_window.rangeToSegment(0, self.getNumber())
warnings.warn("This is deprecated in favor of `extent` property",
DeprecationWarning)
return self.extent

def __getitem__(self, i):
def __getitem__(self, i: int) -> np.ndarray:
"""Get ith feature vector"""
return self.data[i]

def __iter__(self):
self.__i = -1
return self

def __next__(self):
def __next__(self) -> Tuple[Segment, np.ndarray]:
self.__i += 1
try:
return self.sliding_window[self.__i], self.data[self.__i]
Expand All @@ -93,7 +109,8 @@ def __next__(self):
def next(self):
return self.__next__()

def iterfeatures(self, window=False):
def iterfeatures(self, window: Optional[bool] = False) \
-> Iterator[Union[Tuple[np.ndarray, Segment], np.ndarray]]:
"""Feature vector iterator
Parameters
Expand All @@ -103,14 +120,19 @@ def iterfeatures(self, window=False):
Default is to only yield feature vector
"""
nSamples = self.data.shape[0]
for i in range(nSamples):
n_samples = self.data.shape[0]
for i in range(n_samples):
if window:
yield self.data[i], self.sliding_window[i]
else:
yield self.data[i]

def crop(self, focus, mode='loose', fixed=None, return_data=True):
def crop(self,
focus: Union[Segment, Timeline],
mode: CropMode = 'loose',
fixed: Optional[float] = None,
return_data: bool = True) \
-> Union[np.ndarray, 'SlidingWindowFeature']:
"""Extract frames
Parameters
Expand Down Expand Up @@ -175,19 +197,19 @@ def crop(self, focus, mode='loose', fixed=None, return_data=True):
[self.data[start: end, :] for start, end in clipped_ranges])
else:
# if all ranges are out of bounds, just return empty data
shape = (0, ) + self.data.shape[1:]
shape = (0,) + self.data.shape[1:]
data = np.empty(shape)

# corner case when 'fixed' duration cropping is requested:
# correct number of samples even with out-of-bounds indices
if fixed is not None:
data = np.vstack([
# repeat first sample as many times as needed
np.tile(self.data[0], (repeat_first, ) + (1,) * n_dimensions),
np.tile(self.data[0], (repeat_first,) + (1,) * n_dimensions),
data,
# repeat last sample as many times as needed
np.tile(self.data[n_samples - 1],
(repeat_last,) + (1, ) * n_dimensions)])
(repeat_last,) + (1,) * n_dimensions)])

# return data
if return_data:
Expand All @@ -206,7 +228,7 @@ def _repr_png_(self):

_HANDLED_TYPES = (np.ndarray, numbers.Number)

def __array__(self):
def __array__(self) -> np.ndarray:
return self.data

def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
Expand Down Expand Up @@ -242,4 +264,5 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):

if __name__ == "__main__":
import doctest

doctest.testmod()
21 changes: 12 additions & 9 deletions pyannote/core/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@

# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr
from pathlib import Path
from typing import Union, TextIO

import simplejson as json
from .utils.types import Resource

PYANNOTE_JSON = 'pyannote'
PYANNOTE_JSON_CONTENT = 'content'
Expand All @@ -47,7 +50,7 @@ def object_hook(d):
return d


def load(fp):
def load(fp: TextIO) -> Resource:
"""Deserialize
Parameters
Expand All @@ -63,7 +66,7 @@ def load(fp):
return json.load(fp, encoding='utf-8', object_hook=object_hook)


def loads(s):
def loads(s: str) -> Resource:
"""Deserialize
Parameters
Expand All @@ -78,12 +81,12 @@ def loads(s):
return json.loads(s, encoding='utf-8', object_hook=object_hook)


def load_from(path):
def load_from(path: Union[str, Path]) -> Resource:
"""Deserialize
Parameters
----------
path : string
path : string or Path
Path to file containing serialized `pyannote.core` data structure
Returns
Expand All @@ -95,21 +98,21 @@ def load_from(path):
return load(fp)


def dump(resource, fp):
def dump(resource: Resource, fp: TextIO):
"""Serialize
Parameters
----------
resource : `pyannote.core` data structure
Resource to deserialize
Resource to serialize
fp : file
File in which `resource` serialization is written
"""

json.dump(resource, fp, encoding='utf-8', for_json=True)


def dumps(resource):
def dumps(resource: Resource) -> str:
"""Serialize to string
Parameters
Expand All @@ -124,13 +127,13 @@ def dumps(resource):
return json.dumps(resource, encoding='utf-8', for_json=True)


def dump_to(resource, path):
def dump_to(resource: Resource, path: Union[str, Path]):
"""Serialize
Parameters
----------
resource : `pyannote.core` data structure
Resource to deserialize
Resource to serialize
path : string
Path to file in which `resource` serialization is written
"""
Expand Down
Loading

0 comments on commit 1380273

Please sign in to comment.