kopia lustrzana https://github.com/corrscope/corrscope
Switch to new Black version (21.6b0), reformat project (#374)
I also fixed pre-commit hooks on machines without Python 3.6 installed. I don't like how Black now removes leading and trailing spaces in doc comments, but it is what it is.pull/375/head
rodzic
e89fd11325
commit
a655f562f8
|
@ -1,7 +1,7 @@
|
|||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: stable
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.6b0 # Replace by any tag/version: https://github.com/psf/black/tags
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.6
|
||||
exclude: 'utils/scipy/'
|
||||
- id: black
|
||||
language_version: python3 # Should be a command that runs python3.6+
|
||||
exclude: 'utils/scipy/'
|
||||
|
|
|
@ -170,7 +170,7 @@ def copy_config(obj: T) -> T:
|
|||
|
||||
|
||||
class DumpableAttrs:
|
||||
""" Marks class as attrs, and enables YAML dumping (excludes default fields).
|
||||
"""Marks class as attrs, and enables YAML dumping (excludes default fields).
|
||||
|
||||
It is subclassed for
|
||||
(statically-typed key-value objects which will be dumped/loaded as YAML files).
|
||||
|
@ -292,8 +292,8 @@ class DumpableAttrs:
|
|||
|
||||
# SafeRepresenter.represent_yaml_object() uses __getstate__ to dump objects.
|
||||
def __getstate__(self) -> Dict[str, Any]:
|
||||
""" Removes all fields with default values, but not found in
|
||||
self.always_dump. """
|
||||
"""Removes all fields with default values, but not found in
|
||||
self.always_dump."""
|
||||
|
||||
always_dump = self.__always_dump
|
||||
dump_all = "*" in always_dump
|
||||
|
@ -356,8 +356,8 @@ class DumpableAttrs:
|
|||
# If called via instance, cls == type(self).
|
||||
@classmethod
|
||||
def new_from_state(cls: Type[T], state: Dict[str, Any]) -> T:
|
||||
""" Redirect `Alias(key)=value` to `key=value`.
|
||||
Then call the dataclass constructor (to validate parameters). """
|
||||
"""Redirect `Alias(key)=value` to `key=value`.
|
||||
Then call the dataclass constructor (to validate parameters)."""
|
||||
|
||||
cls_name = cls.__name__
|
||||
fields = attr.fields_dict(cls)
|
||||
|
@ -486,14 +486,14 @@ class TypedEnumDump(Enum):
|
|||
|
||||
|
||||
class CorrError(ValueError):
|
||||
""" Error caused by invalid end-user input (via YAML/GUI config).
|
||||
(Should be) caught by GUI and displayed to user. """
|
||||
"""Error caused by invalid end-user input (via YAML/GUI config).
|
||||
(Should be) caught by GUI and displayed to user."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CorrWarning(UserWarning):
|
||||
""" Warning about deprecated end-user config (YAML/GUI).
|
||||
(Should be) caught by GUI and displayed to user. """
|
||||
"""Warning about deprecated end-user config (YAML/GUI).
|
||||
(Should be) caught by GUI and displayed to user."""
|
||||
|
||||
pass
|
||||
|
|
|
@ -41,7 +41,7 @@ class Config(
|
|||
trigger_stereo render_stereo
|
||||
""",
|
||||
):
|
||||
""" Default values indicate optional attributes. """
|
||||
"""Default values indicate optional attributes."""
|
||||
|
||||
master_audio: Optional[str]
|
||||
begin_time: float = with_units("s", default=0)
|
||||
|
@ -63,11 +63,11 @@ class Config(
|
|||
|
||||
# Both before_* functions should be idempotent, AKA calling twice does no harm.
|
||||
def before_preview(self) -> None:
|
||||
""" Called *once* before preview. Does nothing. """
|
||||
"""Called *once* before preview. Does nothing."""
|
||||
self.render.before_preview()
|
||||
|
||||
def before_record(self) -> None:
|
||||
""" Called *once* before recording video. Force high-quality rendering. """
|
||||
"""Called *once* before recording video. Force high-quality rendering."""
|
||||
self.render_subfps = 1
|
||||
self.trigger_subsampling = 1
|
||||
self.render_subsampling = 1
|
||||
|
@ -102,7 +102,7 @@ _FPS = 60 # f_s
|
|||
|
||||
|
||||
def template_config(**kwargs) -> Config:
|
||||
""" Default template values do NOT indicate optional attributes. """
|
||||
"""Default template values do NOT indicate optional attributes."""
|
||||
cfg = Config(
|
||||
master_audio="",
|
||||
fps=_FPS,
|
||||
|
@ -149,7 +149,7 @@ class Arguments:
|
|||
|
||||
class CorrScope:
|
||||
def __init__(self, cfg: Config, arg: Arguments):
|
||||
""" cfg is mutated!
|
||||
"""cfg is mutated!
|
||||
Recording config is triggered if any FFmpegOutputConfig is found.
|
||||
Preview mode is triggered if all outputs are FFplay or others.
|
||||
"""
|
||||
|
|
|
@ -490,7 +490,7 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
return False
|
||||
|
||||
def on_action_preview(self):
|
||||
""" Launch CorrScope and ffplay. """
|
||||
"""Launch CorrScope and ffplay."""
|
||||
if self.corr_thread is not None:
|
||||
error_msg = self.tr("Cannot preview, another {} is active").format(
|
||||
self.preview_or_render
|
||||
|
@ -502,7 +502,7 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
self.play_thread(outputs, PreviewOrRender.preview, dlg=None)
|
||||
|
||||
def on_action_render(self):
|
||||
""" Get file name. Then show a progress dialog while rendering to file. """
|
||||
"""Get file name. Then show a progress dialog while rendering to file."""
|
||||
if self.corr_thread is not None:
|
||||
error_msg = self.tr("Cannot render to file, another {} is active").format(
|
||||
self.preview_or_render
|
||||
|
@ -750,7 +750,7 @@ class CorrProgressDialog(qw.QProgressDialog):
|
|||
def run_on_ui_thread(
|
||||
bound_slot: MethodType, types: Tuple[type, ...]
|
||||
) -> Callable[..., None]:
|
||||
""" Runs an object's slot on the object's own thread.
|
||||
"""Runs an object's slot on the object's own thread.
|
||||
It's terrible code but it works (as long as the slot has no return value).
|
||||
"""
|
||||
qmo = qc.QMetaObject
|
||||
|
@ -1000,13 +1000,13 @@ nope = qc.QVariant()
|
|||
|
||||
|
||||
class ChannelModel(qc.QAbstractTableModel):
|
||||
""" Design based off
|
||||
"""Design based off
|
||||
https://doc.qt.io/qt-5/model-view-programming.html#a-read-only-example-model and
|
||||
https://doc.qt.io/qt-5/model-view-programming.html#model-subclassing-reference
|
||||
"""
|
||||
|
||||
def __init__(self, channels: List[ChannelConfig]):
|
||||
""" Mutates `channels` and `line_color` for convenience. """
|
||||
"""Mutates `channels` and `line_color` for convenience."""
|
||||
super().__init__()
|
||||
self.channels = channels
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ def _call_all(updaters: List[WidgetUpdater]):
|
|||
|
||||
# Data binding presentation-model
|
||||
class PresentationModel(qc.QObject):
|
||||
""" Key-value MVP presentation-model.
|
||||
"""Key-value MVP presentation-model.
|
||||
|
||||
Qt's built-in model-view framework expects all models to
|
||||
take the form of a database-style numbered [row, column] structure,
|
||||
|
@ -169,7 +169,7 @@ class BoundWidget(QWidget):
|
|||
# TODO unbind_widget(), model.update_widget[path].remove(self.cfg2gui)?
|
||||
|
||||
def calc_error_palette(self) -> QPalette:
|
||||
""" Palette with red background, used for widgets with invalid input. """
|
||||
"""Palette with red background, used for widgets with invalid input."""
|
||||
error_palette = QPalette(self.palette())
|
||||
|
||||
bg = error_palette.color(QPalette.Base)
|
||||
|
@ -184,7 +184,7 @@ class BoundWidget(QWidget):
|
|||
# Feel free to improve the naming.
|
||||
|
||||
def cfg2gui(self) -> None:
|
||||
""" Update the widget without triggering signals.
|
||||
"""Update the widget without triggering signals.
|
||||
|
||||
When the presentation pmodel updates dependent widget 1,
|
||||
the pmodel (not widget 1) is responsible for updating other
|
||||
|
@ -208,7 +208,7 @@ class BoundWidget(QWidget):
|
|||
def blend_colors(
|
||||
color1: QColor, color2: QColor, ratio: float, gamma: float = 2
|
||||
) -> QColor:
|
||||
""" Blends two colors in linear color space.
|
||||
"""Blends two colors in linear color space.
|
||||
Produces better results on both light and dark themes,
|
||||
than integer blending (which is too dark).
|
||||
"""
|
||||
|
|
|
@ -26,7 +26,7 @@ T = TypeVar("T")
|
|||
|
||||
|
||||
class Locked(Generic[T]):
|
||||
""" Based off https://stackoverflow.com/a/37606669 """
|
||||
"""Based off https://stackoverflow.com/a/37606669"""
|
||||
|
||||
def __init__(self, obj: T):
|
||||
super().__init__()
|
||||
|
|
|
@ -76,7 +76,7 @@ class ShortcutButton(qw.QPushButton):
|
|||
scoped_shortcut: QShortcut
|
||||
|
||||
def add_shortcut(self, scope: qw.QWidget, shortcut: str) -> None:
|
||||
""" Adds shortcut and tooltip. """
|
||||
"""Adds shortcut and tooltip."""
|
||||
self.scoped_shortcut = new_shortcut(shortcut, scope, self.click)
|
||||
|
||||
parsed_keys: QKeySequence = self.scoped_shortcut.key()
|
||||
|
|
|
@ -49,7 +49,7 @@ class Output(ABC):
|
|||
|
||||
@abstractmethod
|
||||
def write_frame(self, frame: ByteBuffer) -> Optional[_Stop]:
|
||||
""" Output a Numpy ndarray. """
|
||||
"""Output a Numpy ndarray."""
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
|
@ -62,7 +62,7 @@ class Output(ABC):
|
|||
|
||||
|
||||
def register_output(
|
||||
config_t: Type[IOutputConfig]
|
||||
config_t: Type[IOutputConfig],
|
||||
) -> Callable[[Type[Output]], Type[Output]]:
|
||||
def inner(output_t: Type[Output]):
|
||||
config_t.cls = output_t
|
||||
|
@ -130,7 +130,7 @@ def ffmpeg_input_audio(audio_path: str) -> List[str]:
|
|||
|
||||
class PipeOutput(Output):
|
||||
def open(self, *pipeline: subprocess.Popen) -> None:
|
||||
""" Called by __init__ with a Popen pipeline to ffmpeg/ffplay. """
|
||||
"""Called by __init__ with a Popen pipeline to ffmpeg/ffplay."""
|
||||
if len(pipeline) == 0:
|
||||
raise TypeError("must provide at least one Popen argument to popens")
|
||||
|
||||
|
@ -220,7 +220,9 @@ class FFmpegOutputConfig(IOutputConfig):
|
|||
path: Optional[str]
|
||||
args: str = ""
|
||||
|
||||
video_template: str = "-c:v libx264 -crf 18 -preset superfast -pix_fmt yuv420p -movflags faststart"
|
||||
video_template: str = (
|
||||
"-c:v libx264 -crf 18 -preset superfast -pix_fmt yuv420p -movflags faststart"
|
||||
)
|
||||
audio_template: str = "-c:a aac -b:a 384k"
|
||||
|
||||
|
||||
|
|
|
@ -195,11 +195,11 @@ class RendererConfig(
|
|||
|
||||
# Both before_* functions should be idempotent, AKA calling twice does no harm.
|
||||
def before_preview(self) -> None:
|
||||
""" Called *once* before preview. Does nothing. """
|
||||
"""Called *once* before preview. Does nothing."""
|
||||
pass
|
||||
|
||||
def before_record(self) -> None:
|
||||
""" Called *once* before recording video. Eliminates res_divisor. """
|
||||
"""Called *once* before recording video. Eliminates res_divisor."""
|
||||
self.res_divisor = 1
|
||||
|
||||
|
||||
|
@ -730,7 +730,7 @@ class AbstractMatplotlibRenderer(_RendererBackend, ABC):
|
|||
bg_cache: Any # "matplotlib.backends._backend_agg.BufferRegion"
|
||||
|
||||
def _save_background(self) -> None:
|
||||
""" Draw static background. """
|
||||
"""Draw static background."""
|
||||
# https://stackoverflow.com/a/8956211
|
||||
# https://matplotlib.org/api/animation_api.html#funcanimation
|
||||
fig = self._fig
|
||||
|
@ -739,7 +739,7 @@ class AbstractMatplotlibRenderer(_RendererBackend, ABC):
|
|||
self.bg_cache = fig.canvas.copy_from_bbox(fig.bbox)
|
||||
|
||||
def _redraw_over_background(self) -> None:
|
||||
""" Redraw animated elements of the image. """
|
||||
"""Redraw animated elements of the image."""
|
||||
|
||||
# Both FigureCanvasAgg and FigureCanvasCairo, but not FigureCanvasBase,
|
||||
# support restore_region().
|
||||
|
|
|
@ -14,7 +14,7 @@ __all__ = ["appdata_dir", "PATH_dir", "get_ffmpeg_url", "MissingFFmpegError"]
|
|||
|
||||
|
||||
def prepend(dic: MutableMapping[str, str], _key: List[str], prefix: str) -> None:
|
||||
""" Dubiously readable syntactic sugar for prepending to a string in a dict. """
|
||||
"""Dubiously readable syntactic sugar for prepending to a string in a dict."""
|
||||
key = _key[0]
|
||||
dic[key] = prefix + dic[key]
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ class LogFreqSpectrum(DummySpectrum):
|
|||
self.n_fencepost = len(note_fenceposts)
|
||||
|
||||
def calc_spectrum(self, data: np.ndarray) -> np.ndarray:
|
||||
""" Unfortunately converting to FLOAT (single) adds too much overhead.
|
||||
"""Unfortunately converting to FLOAT (single) adds too much overhead.
|
||||
|
||||
Input: Time-domain signal to be analyzed.
|
||||
Output: Frequency-domain spectrum with exponentially-spaced notes.
|
||||
|
@ -144,7 +144,7 @@ class LogFreqSpectrum(DummySpectrum):
|
|||
|
||||
|
||||
def split(data: np.ndarray, fenceposts: Sequence[FFTIndex]) -> List[np.ndarray]:
|
||||
""" Based off np.split(), but faster.
|
||||
"""Based off np.split(), but faster.
|
||||
Unlike np.split, does not include data before fenceposts[0] or after fenceposts[-1].
|
||||
"""
|
||||
sub_arys = []
|
||||
|
|
|
@ -65,7 +65,7 @@ class PostTriggerConfig(_TriggerConfig, KeywordAttrs):
|
|||
|
||||
|
||||
def register_trigger(config_t: Type[_TriggerConfig]):
|
||||
""" @register_trigger(FooTriggerConfig)
|
||||
"""@register_trigger(FooTriggerConfig)
|
||||
def FooTrigger(): ...
|
||||
"""
|
||||
|
||||
|
@ -196,8 +196,8 @@ class MainTrigger(_Trigger, ABC):
|
|||
|
||||
|
||||
class PostTrigger(_Trigger, ABC):
|
||||
""" A post-processing trigger should have stride=1,
|
||||
and no more post triggers. This is subject to change. """
|
||||
"""A post-processing trigger should have stride=1,
|
||||
and no more post triggers. This is subject to change."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -366,7 +366,7 @@ class CorrelationTrigger(MainTrigger):
|
|||
self._spectrum_calc = DummySpectrum()
|
||||
|
||||
def _calc_lag_prevention(self) -> np.ndarray:
|
||||
""" Returns input-data window,
|
||||
"""Returns input-data window,
|
||||
which zeroes out all data older than 1-ish frame old.
|
||||
See https://github.com/corrscope/corrscope/wiki/Correlation-Trigger
|
||||
"""
|
||||
|
@ -403,7 +403,7 @@ class CorrelationTrigger(MainTrigger):
|
|||
return data_taper
|
||||
|
||||
def _calc_step(self) -> np.ndarray:
|
||||
""" Step function used for approximate edge triggering. """
|
||||
"""Step function used for approximate edge triggering."""
|
||||
|
||||
# Increasing buffer_falloff (width of buffer)
|
||||
# causes buffer to affect triggering, more than the step function.
|
||||
|
@ -422,7 +422,7 @@ class CorrelationTrigger(MainTrigger):
|
|||
return step
|
||||
|
||||
def _calc_slope_finder(self, period: float) -> np.ndarray:
|
||||
""" Called whenever period changes substantially.
|
||||
"""Called whenever period changes substantially.
|
||||
Returns a kernel to be correlated with input data,
|
||||
to find positive slopes."""
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ def get_profile_dump_name(prefix: str) -> str:
|
|||
|
||||
|
||||
def add_numeric_suffixes(s: str) -> Iterator[str]:
|
||||
""" f('foo')
|
||||
"""f('foo')
|
||||
yields 'foo', 'foo2', 'foo3'...
|
||||
"""
|
||||
yield s
|
||||
|
|
|
@ -25,7 +25,7 @@ metadata_key = "build_metadata"
|
|||
|
||||
|
||||
def get_version() -> str:
|
||||
""" Called at runtime (maybe pyinstaller time too).
|
||||
"""Called at runtime (maybe pyinstaller time too).
|
||||
Depends on pyinstaller_write_version and filesystem.
|
||||
"""
|
||||
is_installer = hasattr(sys, "frozen")
|
||||
|
@ -41,7 +41,7 @@ def get_version() -> str:
|
|||
|
||||
|
||||
def pyinstaller_write_version() -> str:
|
||||
""" Returns version.
|
||||
"""Returns version.
|
||||
|
||||
Called only at pyinstaller time.
|
||||
Writes to filesystem, does NOT call get_version().
|
||||
|
|
|
@ -15,7 +15,7 @@ from corrscope.utils.windows import rightpad
|
|||
|
||||
@enum.unique
|
||||
class Flatten(str, TypedEnumDump):
|
||||
""" How to flatten a stereo signal. (Channels beyond first 2 are ignored.)
|
||||
"""How to flatten a stereo signal. (Channels beyond first 2 are ignored.)
|
||||
|
||||
Flatten(0) == Flatten.Stereo == Flatten['Stereo']
|
||||
"""
|
||||
|
@ -54,7 +54,7 @@ FlattenOrStr = Union[Flatten, str]
|
|||
|
||||
|
||||
def calc_flatten_matrix(flatten: FlattenOrStr, stereo_nchan: int) -> np.ndarray:
|
||||
""" Raises CorrError on invalid input.
|
||||
"""Raises CorrError on invalid input.
|
||||
|
||||
If flatten is Flatten.Stereo, returns shape=(nchan,nchan) identity matrix.
|
||||
- (N,nchan) @ (nchan,nchan) = (N,nchan).
|
||||
|
@ -206,7 +206,7 @@ class Wave:
|
|||
return new
|
||||
|
||||
def __getitem__(self, index: Union[int, slice]) -> np.ndarray:
|
||||
""" Copies self.data[item], converted to a FLOAT within range [-1, 1). """
|
||||
"""Copies self.data[item], converted to a FLOAT within range [-1, 1)."""
|
||||
# subok=False converts data from memmap (slow) to ndarray (faster).
|
||||
data: np.ndarray = self.data[index].astype(FLOAT, subok=False, copy=True)
|
||||
|
||||
|
@ -222,7 +222,7 @@ class Wave:
|
|||
return data
|
||||
|
||||
def _get(self, begin: int, end: int, subsampling: int) -> np.ndarray:
|
||||
""" Copies self.data[begin:end] with zero-padding. """
|
||||
"""Copies self.data[begin:end] with zero-padding."""
|
||||
if 0 <= begin and end <= self.nsamp:
|
||||
return self[begin:end:subsampling]
|
||||
|
||||
|
@ -258,12 +258,12 @@ class Wave:
|
|||
return out
|
||||
|
||||
def get_around(self, sample: int, return_nsamp: int, stride: int) -> np.ndarray:
|
||||
""" Returns `return_nsamp` samples, centered around `sample`,
|
||||
"""Returns `return_nsamp` samples, centered around `sample`,
|
||||
sampled with spacing `stride`.
|
||||
result[N//2] == self[sample].
|
||||
See designNotes.md and CorrelationTrigger docstring.
|
||||
|
||||
Copies self.data[...]. """
|
||||
Copies self.data[...]."""
|
||||
|
||||
begin = sample - (return_nsamp // 2) * stride
|
||||
end = begin + return_nsamp * stride
|
||||
|
|
|
@ -16,19 +16,19 @@ from {module} import {func}
|
|||
|
||||
# public:
|
||||
def dcover():
|
||||
""" Run coverage and diff-cover."""
|
||||
"""Run coverage and diff-cover."""
|
||||
cover()
|
||||
diff()
|
||||
|
||||
|
||||
def rcover():
|
||||
""" Run coverage and report. """
|
||||
"""Run coverage and report."""
|
||||
cover()
|
||||
report()
|
||||
|
||||
|
||||
def hcover():
|
||||
""" Run coverage and open HTML. """
|
||||
"""Run coverage and open HTML."""
|
||||
cover()
|
||||
html()
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ def test_config_channel_integration(
|
|||
override_label: bool,
|
||||
mocker: MockFixture,
|
||||
):
|
||||
""" (Tautologically) verify:
|
||||
"""(Tautologically) verify:
|
||||
- channel. r_samp (given cfg)
|
||||
- channel.t/r_stride (given cfg.*_subsampling/*_width)
|
||||
- trigger._tsamp, _stride
|
||||
|
|
|
@ -34,7 +34,7 @@ def call_main(argv):
|
|||
|
||||
|
||||
def yaml_sink(_mocker, command: str):
|
||||
""" Mocks yaml.dump() and returns call args. Also tests dumping and loading. """
|
||||
"""Mocks yaml.dump() and returns call args. Also tests dumping and loading."""
|
||||
with mock.patch.object(yaml, "dump") as dump:
|
||||
|
||||
argv = shlex.split(command) + ["-w"]
|
||||
|
@ -87,7 +87,7 @@ def test_no_files(any_sink, mocker):
|
|||
|
||||
@pytest.mark.parametrize("wav_dir", ". tests".split())
|
||||
def test_file_dirs(any_sink, mocker, wav_dir):
|
||||
""" Ensure loading files from `dir` places `dir/*.wav` in config. """
|
||||
"""Ensure loading files from `dir` places `dir/*.wav` in config."""
|
||||
wavs = Path(wav_dir).glob("*.wav")
|
||||
wavs = sorted(str(x) for x in wavs)
|
||||
|
||||
|
@ -102,8 +102,8 @@ def q(path: Path) -> str:
|
|||
|
||||
|
||||
def test_write_dir(mocker):
|
||||
""" Loading `--audio another/dir` should write YAML to current dir.
|
||||
Writing YAML to audio dir: causes relative paths (relative to pwd) to break. """
|
||||
"""Loading `--audio another/dir` should write YAML to current dir.
|
||||
Writing YAML to audio dir: causes relative paths (relative to pwd) to break."""
|
||||
|
||||
audio_path = Path("tests/sine440.wav")
|
||||
arg_str = f"tests -a {q(audio_path)}"
|
||||
|
@ -125,8 +125,8 @@ def test_write_dir(mocker):
|
|||
|
||||
@pytest.mark.usefixtures("Popen")
|
||||
def test_load_yaml_another_dir(mocker, Popen):
|
||||
""" YAML file located in `another/dir` should resolve `master_audio`, `channels[].
|
||||
wav_path`, and video `path` from `another/dir`. """
|
||||
"""YAML file located in `another/dir` should resolve `master_audio`, `channels[].
|
||||
wav_path`, and video `path` from `another/dir`."""
|
||||
|
||||
subdir = "tests"
|
||||
wav = "sine440.wav"
|
||||
|
|
|
@ -126,10 +126,10 @@ b: b
|
|||
|
||||
|
||||
def test_dump_default_factory():
|
||||
""" Ensure default factories are not dumped, unless attribute present
|
||||
"""Ensure default factories are not dumped, unless attribute present
|
||||
in `always_dump`.
|
||||
|
||||
Based on `attrs.Factory`. """
|
||||
Based on `attrs.Factory`."""
|
||||
|
||||
class Config(DumpableAttrs):
|
||||
# Equivalent to attr.ib(factory=str)
|
||||
|
@ -233,7 +233,7 @@ c: 3
|
|||
|
||||
|
||||
def test_dump_load_aliases():
|
||||
""" Ensure dumping and loading `xx=Alias('x')` works.
|
||||
"""Ensure dumping and loading `xx=Alias('x')` works.
|
||||
Ensure loading `{x=1, xx=1}` raises an error.
|
||||
Does not check constructor `Config(xx=1)`."""
|
||||
|
||||
|
@ -272,7 +272,7 @@ xx: 1
|
|||
|
||||
|
||||
def test_dump_load_ignored():
|
||||
""" Ensure loading `xx=Ignored` works.
|
||||
"""Ensure loading `xx=Ignored` works.
|
||||
Does not check constructor `Config(xx=1)`.
|
||||
"""
|
||||
|
||||
|
@ -300,7 +300,7 @@ xx: 1
|
|||
|
||||
|
||||
def test_load_argument_validation():
|
||||
""" Ensure that loading config via YAML catches missing parameters. """
|
||||
"""Ensure that loading config via YAML catches missing parameters."""
|
||||
|
||||
class Config(DumpableAttrs):
|
||||
a: int
|
||||
|
@ -332,8 +332,8 @@ bar: 2
|
|||
|
||||
|
||||
def test_load_post_init():
|
||||
""" yaml.load() does not natively call __init__.
|
||||
So DumpableAttrs modifies __setstate__ to call __attrs_post_init__. """
|
||||
"""yaml.load() does not natively call __init__.
|
||||
So DumpableAttrs modifies __setstate__ to call __attrs_post_init__."""
|
||||
|
||||
class Foo(DumpableAttrs):
|
||||
foo: int
|
||||
|
|
|
@ -29,7 +29,7 @@ def separator(s: str) -> str:
|
|||
|
||||
|
||||
def test_rgetattr(separator):
|
||||
""" Test to ensure recursive model access works.
|
||||
"""Test to ensure recursive model access works.
|
||||
GUI elements are named f"prefix{_}" "recursive{_}attr" and bind to recursive.attr.
|
||||
|
||||
https://stackoverflow{_}com/a/31174427/
|
||||
|
|
|
@ -63,7 +63,7 @@ def sine440_config():
|
|||
## Begin tests
|
||||
# Calls MatplotlibRenderer, FFmpegOutput, FFmpeg.
|
||||
def test_render_output():
|
||||
""" Ensure rendering to output does not raise exceptions. """
|
||||
"""Ensure rendering to output does not raise exceptions."""
|
||||
datas = [RENDER_Y_ZEROS]
|
||||
|
||||
renderer = Renderer(CFG.render, CFG.layout, datas, None, None)
|
||||
|
@ -91,7 +91,7 @@ def test_output():
|
|||
# Calls FFplayOutput, mocks Popen.
|
||||
@pytest.mark.usefixtures("Popen")
|
||||
def test_close_output(Popen):
|
||||
""" FFplayOutput unit test: Ensure ffmpeg and ffplay are terminated when Python
|
||||
"""FFplayOutput unit test: Ensure ffmpeg and ffplay are terminated when Python
|
||||
exceptions occur.
|
||||
"""
|
||||
|
||||
|
@ -107,7 +107,7 @@ def test_close_output(Popen):
|
|||
|
||||
# Calls CorrScope, mocks FFmpegOutput.
|
||||
def test_corrscope_main_uses_contextmanager(mocker: "pytest_mock.MockFixture"):
|
||||
""" Ensure CorrScope() main wraps output in context manager. """
|
||||
"""Ensure CorrScope() main wraps output in context manager."""
|
||||
FFmpegOutput = mocker.patch.object(FFmpegOutputConfig, "cls")
|
||||
output = FFmpegOutput.return_value
|
||||
|
||||
|
@ -124,7 +124,7 @@ def test_corrscope_main_uses_contextmanager(mocker: "pytest_mock.MockFixture"):
|
|||
# Calls FFplayOutput, mocks Popen.
|
||||
@pytest.mark.usefixtures("Popen")
|
||||
def test_terminate_ffplay(Popen):
|
||||
""" FFplayOutput unit test: Ensure ffmpeg and ffplay are terminated when Python
|
||||
"""FFplayOutput unit test: Ensure ffmpeg and ffplay are terminated when Python
|
||||
exceptions occur.
|
||||
"""
|
||||
|
||||
|
@ -142,8 +142,8 @@ def test_terminate_ffplay(Popen):
|
|||
# Integration: Calls CorrScope, mocks Popen.
|
||||
@pytest.mark.usefixtures("Popen")
|
||||
def test_corr_terminate_ffplay(Popen, mocker: "pytest_mock.MockFixture"):
|
||||
""" Integration test: Ensure corrscope calls terminate() on ffmpeg and ffplay when
|
||||
Python exceptions occur. """
|
||||
"""Integration test: Ensure corrscope calls terminate() on ffmpeg and ffplay when
|
||||
Python exceptions occur."""
|
||||
|
||||
cfg = sine440_config()
|
||||
corr = CorrScope(cfg, Arguments(".", [FFplayOutputConfig()]))
|
||||
|
@ -227,8 +227,8 @@ def test_corr_terminate_works(test):
|
|||
@pytest.mark.usefixtures("Popen")
|
||||
@pytest.mark.parametrize("errno_id", [errno.EPIPE, errno.EINVAL])
|
||||
def test_closing_ffplay_stops_main(Popen, errno_id):
|
||||
""" Closing FFplay should make FFplayOutput.write_frame() return Stop
|
||||
to main loop. """
|
||||
"""Closing FFplay should make FFplayOutput.write_frame() return Stop
|
||||
to main loop."""
|
||||
|
||||
# Create mocks.
|
||||
exc = OSError(errno_id, "Simulated ffplay-closed error")
|
||||
|
@ -264,7 +264,7 @@ def test_corr_output_without_audio():
|
|||
|
||||
# Test framerate subsampling
|
||||
def test_render_subfps_one():
|
||||
""" Ensure video gets rendered when render_subfps=1.
|
||||
"""Ensure video gets rendered when render_subfps=1.
|
||||
This test fails if ceildiv is used to calculate `ahead`.
|
||||
"""
|
||||
from corrscope.outputs import IOutputConfig, Output, register_output
|
||||
|
@ -294,7 +294,7 @@ def test_render_subfps_one():
|
|||
|
||||
|
||||
def test_render_subfps_non_integer(mocker: "pytest_mock.MockFixture"):
|
||||
""" Ensure we output non-integer subfps as fractions,
|
||||
"""Ensure we output non-integer subfps as fractions,
|
||||
and that ffmpeg doesn't crash.
|
||||
TODO does ffmpeg understand decimals??
|
||||
"""
|
||||
|
@ -327,7 +327,7 @@ def test_render_subfps_non_integer(mocker: "pytest_mock.MockFixture"):
|
|||
|
||||
## Tests for Output-dependent performance options
|
||||
def cfg_192x108():
|
||||
""" Return config which reduces rendering workload when previewing. """
|
||||
"""Return config which reduces rendering workload when previewing."""
|
||||
cfg = sine440_config()
|
||||
|
||||
# Skip frames.
|
||||
|
@ -357,8 +357,8 @@ NO_FFMPEG = [[], [FFplayOutputConfig()]]
|
|||
@pytest.mark.usefixtures("Popen") # Prevents FFplayOutput from launching processes.
|
||||
@pytest.mark.parametrize("outputs", NO_FFMPEG)
|
||||
def test_preview_performance(Popen, mocker: "pytest_mock.MockFixture", outputs):
|
||||
""" Ensure performance optimizations enabled
|
||||
if all outputs are FFplay or others. """
|
||||
"""Ensure performance optimizations enabled
|
||||
if all outputs are FFplay or others."""
|
||||
get_frame = mocker.spy(Renderer, "get_frame")
|
||||
previews, records = previews_records(mocker)
|
||||
|
||||
|
@ -391,8 +391,8 @@ YES_FFMPEG = [l + [FFmpegOutputConfig(None)] for l in NO_FFMPEG]
|
|||
@pytest.mark.usefixtures("Popen")
|
||||
@pytest.mark.parametrize("outputs", YES_FFMPEG)
|
||||
def test_record_performance(Popen, mocker: "pytest_mock.MockFixture", outputs):
|
||||
""" Ensure performance optimizations disabled
|
||||
if any FFmpegOutputConfig is found. """
|
||||
"""Ensure performance optimizations disabled
|
||||
if any FFmpegOutputConfig is found."""
|
||||
get_frame = mocker.spy(Renderer, "get_frame")
|
||||
previews, records = previews_records(mocker)
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ GRID_NPIXEL = WIDTH
|
|||
|
||||
@all_colors
|
||||
def test_default_colors(appear: Appearance, data):
|
||||
""" Test the default background/foreground colors. """
|
||||
"""Test the default background/foreground colors."""
|
||||
cfg = get_renderer_config(appear)
|
||||
lcfg = LayoutConfig(orientation=ORIENTATION)
|
||||
datas = [data] * NPLOTS
|
||||
|
@ -208,7 +208,7 @@ def test_default_colors(appear: Appearance, data):
|
|||
|
||||
@all_colors
|
||||
def test_line_colors(appear: Appearance, data):
|
||||
""" Test channel-specific line color overrides """
|
||||
"""Test channel-specific line color overrides"""
|
||||
cfg = get_renderer_config(appear)
|
||||
lcfg = LayoutConfig(orientation=ORIENTATION)
|
||||
datas = [data] * NPLOTS
|
||||
|
|
|
@ -240,7 +240,7 @@ def test_wave_subsampling_off_by_1(is_odd: bool):
|
|||
|
||||
|
||||
def test_stereo_doesnt_overflow():
|
||||
""" Ensure loud stereo tracks do not overflow. """
|
||||
"""Ensure loud stereo tracks do not overflow."""
|
||||
wave = Wave("tests/stereo in-phase.wav")
|
||||
|
||||
samp = 100
|
||||
|
|
Ładowanie…
Reference in New Issue