Skip to content

Commit

Permalink
Merge pull request #54 from ReadAlongs/PEP561
Browse files Browse the repository at this point in the history
Add type hints to Python API
  • Loading branch information
dhdaines authored Jan 17, 2023
2 parents e8ee7c7 + 3b995d8 commit 8944fe0
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 110 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ recursive-exclude * *.py[co]
recursive-exclude * *~
recursive-exclude * *.orig
recursive-exclude * *.DS_Store
global-include *.pyi
global-include *.typed
3 changes: 3 additions & 0 deletions py/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
extend-ignore = E203
4 changes: 2 additions & 2 deletions py/_soundswallower.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ cdef class Vad:
integers). Must be of length `frame_bytes`
(in bytes).
Returns:
boolean: Classification as speech or not speech.
bool: Classification as speech or not speech.
Raises:
IndexError: `buf` is of invalid size.
ValueError: Other internal VAD error.
Expand Down Expand Up @@ -953,7 +953,7 @@ cdef class Endpointer:

@property
def frame_length(self):
"""float: Length of a frame in secondsq (*may be different from the one
"""float: Length of a frame in seconds (*may be different from the one
requested in the constructor*!)"""
return endpointer_frame_length(self._ep)

Expand Down
24 changes: 19 additions & 5 deletions py/soundswallower/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
print("Word %s from %.3f to %.3f" % (word, start, end))
"""
import wave
import collections
import os
import wave
from typing import Optional, Tuple

from ._soundswallower import Config # noqa: F401
from ._soundswallower import Decoder # noqa: F401
from ._soundswallower import Endpointer # noqa: F401
from ._soundswallower import FsgModel # noqa: F401
from ._soundswallower import Vad # noqa: F401
from ._soundswallower import Endpointer # noqa: F401


def get_model_path(subpath=None):
def get_model_path(subpath: Optional[str] = None) -> str:
"""Return path to the model directory, or optionally, a specific file
or directory within it.
Expand All @@ -35,14 +36,14 @@ def get_model_path(subpath=None):
Returns:
The requested path within the model directory."""
model_path = os.path.join(os.path.dirname(__file__), 'model')
model_path = os.path.join(os.path.dirname(__file__), "model")
if subpath is not None:
return os.path.join(model_path, subpath)
else:
return model_path


def get_audio_data(input_file):
def get_audio_data(input_file: str) -> Tuple[bytes, Optional[int]]:
"""Try to get single-channel audio data in the most portable way
possible.
Expand Down Expand Up @@ -87,3 +88,16 @@ def get_audio_data(input_file):
Hyp.text.__doc__ = "Recognized text."
Hyp.score.__doc__ = "Best path score."
Hyp.prob.__doc__ = "Posterior probability of hypothesis (often 1.0, sorry)."

__all__ = [
"Config",
"Decoder",
"FsgModel",
"Vad",
"Endpointer",
"Arg",
"Seg",
"Hyp",
"get_model_path",
"get_audio_data",
]
201 changes: 201 additions & 0 deletions py/soundswallower/_soundswallower.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
from typing import Union, Iterator, Tuple, Optional, Sequence, ClassVar
import soundswallower


class Config:
def __init__(self, *args, **kwargs) -> None:
...

@staticmethod
def parse_json(json: Union[bytes, str]) -> "Config":
...

def dumps(self) -> str:
...

def __contains__(self, key: Union[bytes, str]) -> bool:
...

def __getitem__(self, key: Union[bytes, str]) -> Union[str, int, float, bool]:
...

def __setitem__(self, key: Union[bytes, str], val: Union[str, int, float, bool]):
...

def __delitem__(self, key: Union[bytes, str]):
...

def __iter__(self) -> Iterator[str]:
...

def items(self) -> Iterator[Tuple[str, Union[str, int, float, bool]]]:
...

def __len__(self) -> int:
...

def describe(self) -> Iterator[soundswallower.Arg]:
...


class FsgModel:
pass


class Alignment:
pass


class Decoder:
config: Config
cmn: str
hyp: soundswallower.Hyp
seg: Iterator[soundswallower.Seg]
alignment: Alignment
n_frames: int

def __init__(self, *args, **kwargs) -> None:
...

@staticmethod
def create(*args, **kwargs) -> "Decoder":
...

def initialize(self, config: Optional[Config] = ...):
...

def reinit_feat(self, config: Optional[Config] = ...):
...

def update_cmn(self) -> str:
...

def start_utt(self) -> None:
...

def process_raw(
self,
data: bytes,
no_search: bool = ...,
full_utt: bool = ...,
):
...

def end_utt(self) -> None:
...

def add_word(self, word: str, phones: str, update: bool = ...) -> int:
...

def lookup_word(self, word: str) -> int:
...

def read_fsg(self, filename: str) -> FsgModel:
...

def read_jsgf(self, filename: str) -> FsgModel:
...

def create_fsg(
self,
name: str,
start_state: int,
final_state: int,
transitions: Sequence[Tuple],
) -> FsgModel:
...

def parse_jsgf(
self, jsgf_string: Union[bytes, str], toprule: Optional[str] = ...
) -> FsgModel:
...

def set_fsg(self, fsg: FsgModel):
...

def set_jsgf_file(self, filename: str):
...

def set_jsgf_string(self, jsgf_string: Union[bytes, str]):
...

def decode_file(self, input_file: str) -> Tuple[str, Iterator[soundswallower.Seg]]:
...

def dumps(self, start_time: float = ..., align_level: int = ...) -> str:
...

def set_align_text(self, text: str):
...


class Vad:
LOOSE: ClassVar[int]
MEDIUM_LOOSE: ClassVar[int]
MEDIUM_STRICT: ClassVar[int]
STRICT: ClassVar[int]
DEFAULT_SAMPLE_RATE: ClassVar[int]
DEFAULT_FRAME_LENGTH: ClassVar[float]

frame_bytes: int
frame_length: float
sample_rate: int

def __init__(
self, mode: int = ..., sample_rate: int = ..., frame_length: float = ...
) -> None:
...

def is_speech(self, frame: bytes, sample_rate: int = ...) -> bool:
...


class Endpointer:
DEFAULT_WINDOW: ClassVar[float]
DEFAULT_RATIO: ClassVar[float]
frame_bytes: int
frame_length: float
sample_rate: int
in_speech: bool
speech_start: float
speech_end: float

def __init__(
self,
window: float = ...,
ratio: float = ...,
vad_mode: int = ...,
sample_rate: int = ...,
frame_length: float = ...,
) -> None:
...

def process(self, frame: bytes) -> Optional[bytes]:
...

def end_stream(self, frame: bytes) -> Optional[bytes]:
...


class AlignmentEntry:
start: int
duration: int
score: int
name: str

def __iter__(self) -> Iterator["AlignmentEntry"]:
...


class Alignment:
def __iter__(self) -> Iterator[AlignmentEntry]:
...

def words(self) -> Iterator[AlignmentEntry]:
...

def phones(self) -> Iterator[AlignmentEntry]:
...

def states(self) -> Iterator[AlignmentEntry]:
...
Loading

0 comments on commit 8944fe0

Please sign in to comment.