Example #1
0
def test_load_list_dict_type():
    """Fails on ruamel.yaml<0.15.70 (CommentedMap/CommentedSeq)."""
    dic = yaml.load("{}")
    assert isinstance(dic, dict)

    lis = yaml.load("[]")
    assert isinstance(lis, list)
Example #2
0
def test_dump_load_aliases():
    """ Ensure dumping and loading `xx=Alias('x')` works.
    Ensure loading `{x=1, xx=1}` raises an error.
    Does not check constructor `Config(xx=1)`."""
    class Config(DumpableAttrs, kw_only=False):
        x: int
        xx = Alias("x")

    # Test dumping
    assert len(attr.fields(Config)) == 1
    cfg = Config(1)
    s = yaml.dump(cfg)
    assert (s == """\
!Config
x: 1
""")
    assert yaml.load(s) == cfg

    # Test loading
    s = """\
!Config
xx: 1
"""
    assert yaml.load(s) == Config(x=1)

    # Test exception on duplicated parameters.
    s = """\
    !Config
    x: 1
    xx: 1
    """
    with pytest.raises(CorrError):
        yaml.load(s)
Example #3
0
def test_load_trigger_config():
    from corrscope.config import yaml

    # Ensure no exceptions
    yaml.load("""\
!CorrelationTriggerConfig
  trigger_strength: 3
  responsiveness: 0.2
  falloff_width: 2
""")
Example #4
0
def test_load_argument_validation():
    """ Ensure that loading config via YAML catches missing parameters. """
    class Config(DumpableAttrs):
        a: int

    yaml.load("""\
!Config
  a: 1
""")

    with pytest.raises(TypeError):
        yaml.load("!Config {}")
Example #5
0
def test_skip_dump_load():
    """Ensure _fields or init=False are not dumped,
    and don't crash on loading.

    Ensure that init=False fields are not retrieved at all.
    """
    class Foo(DumpableAttrs, always_dump="*"):
        _underscore: int
        _underscore_default: int = 1
        init_false: int = attr.ib(init=False)
        never_initialized: int = attr.ib(init=False)

        def __attrs_post_init__(self):
            self.init_false = 1

    # Ensure fields are not dumped.
    foo = Foo(underscore=1)
    assert (yaml.dump(foo) == """\
!Foo
underscore: 1
underscore_default: 1
""")

    # Ensure init=False fields don't crash on loading.
    evil = """\
!Foo
underscore: 1
_underscore: 2
init_false: 3
"""
    assert yaml.load(evil)._underscore == 1
Example #6
0
def test_ignore_unrecognized_fields():
    """Ensure unrecognized fields yield warning, not exception."""
    class Foo(DumpableAttrs):
        foo: int

    s = """\
!Foo
foo: 1
bar: 2
"""
    with pytest.warns(CorrWarning):
        assert yaml.load(s) == Foo(1)
Example #7
0
def test_dump_load_ignored():
    """ Ensure loading `xx=Ignored` works.
    Does not check constructor `Config(xx=1)`.
    """
    class Config(DumpableAttrs):
        xx = Ignored

    # Test dumping
    assert len(attr.fields(Config)) == 0
    cfg = Config()
    s = yaml.dump(cfg)
    assert (s == """\
!Config {}
""")
    assert yaml.load(s) == cfg

    # Test loading
    s = """\
!Config
xx: 1
"""
    assert yaml.load(s) == Config()
Example #8
0
def test_loading_corrupted_locale_config():
    """
    See corrscope.config.MyYAML.load() docstring.
    """
    runner = click.testing.CliRunner()
    with runner.isolated_filesystem():
        raw_yaml = YAML()
        p = Path("test.yaml")

        before = {"key": "á"}
        raw_yaml.dump(before, p)

        after = yaml.load(p)
        assert after == before
Example #9
0
def test_load_post_init():
    """ yaml.load() does not natively call __init__.
    So DumpableAttrs modifies __setstate__ to call __attrs_post_init__. """
    class Foo(DumpableAttrs):
        foo: int

        def __attrs_post_init__(self):
            self.foo = 99

    s = """\
!Foo
foo: 0
"""
    assert yaml.load(s) == Foo(99)
Example #10
0
def test_load_dump_dict_order():
    s = """\
a: 1
b: 1
c: 1
d: 1
e: 1
z: 1
y: 1
x: 1
w: 1
v: 1
"""
    dic = yaml.load(s)
    assert yaml.dump(dic) == s, yaml.dump(dic)
Example #11
0
    def load_cfg_from_path(self, cfg_path: Path):
        # Bind GUI to dummy config, in case loading cfg_path raises Exception.
        if self.model is None:
            self.load_cfg(template_config(), None)

        assert cfg_path.is_file()
        self.pref.file_dir = str(cfg_path.parent.resolve())

        # Raises YAML structural exceptions
        cfg = yaml.load(cfg_path)

        try:
            # Raises color getter exceptions
            self.load_cfg(cfg, cfg_path)
        except Exception as e:
            # FIXME if error halfway, clear "file path" and load empty model.
            TracebackDialog(self).showMessage(format_stack_trace(e))
            return
Example #12
0
    def load_cfg_from_path(self, cfg_path: Path):
        # Bind GUI to dummy config, in case loading cfg_path raises Exception.
        if self.model is None:
            self.load_cfg(default_config(), None)

        assert cfg_path.is_file()
        self.pref.file_dir = str(cfg_path.parent.resolve())

        try:
            # Raises YAML structural exceptions
            cfg = yaml.load(cfg_path)

            # Raises color getter exceptions
            # FIXME if error halfway, clear "file path" and load empty model.
            self.load_cfg(cfg, cfg_path)

        except Exception as e:
            qw.QMessageBox.critical(self, "Error loading file", str(e))
            return
Example #13
0
def test_unicode_dump_load():
    """
    Crashes on latest ruamel.yaml 0.16.5.
    https://bitbucket.org/ruamel/yaml/issues/316/unicode-encoding-decoding-errors-on

    And affects real users when they save "most recent files" containing Unicode.
    https://github.com/jimbo1qaz/corrscope/issues/308

    Workaround in MyYAML.dump(), to encode as UTF-8 instead of locale.
    """

    runner = click.testing.CliRunner()
    with runner.isolated_filesystem():
        p = Path("test.yaml")

        before = {"key": "á😂"}
        yaml.dump(before, p)
        after = yaml.load(p)
        assert after == before
Example #14
0
def yaml_sink(_mocker, command: str):
    """ 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"]
        call_main(argv)

        dump.assert_called_once()
        (cfg, stream), kwargs = dump.call_args

        assert isinstance(cfg, Config)

    yaml_dump = yaml.dump(cfg)

    # YAML Representer.ignore_aliases() should return True for Enums.
    # If it returns True for other types, "null" is dumped explicitly, which is ugly.
    assert "end_time: null" not in yaml_dump

    cfg_round_trip = yaml.load(yaml_dump)
    assert cfg_round_trip == cfg, yaml_dump

    return (cfg, stream)
Example #15
0
def load_prefs() -> GlobalPrefs:
    try:
        return yaml.load(_PREF_PATH)
    except FileNotFoundError:
        return GlobalPrefs()
Example #16
0
def test_list_slice_assign():
    """Crashes on ruamel.yaml<0.15.55 (CommentedSeq)."""
    lis = yaml.load("[]")
    lis[0:0] = list(range(5))
    lis[2:5] = []
Example #17
0
def main(
    files: Tuple[str],
    # cfg
    audio: Optional[str],
    # gui
    write: bool,
    play: bool,
    render: bool,
    profile: bool,
):
    """Intelligent oscilloscope visualizer for .wav files.

    FILES can be one or more .wav files (or wildcards), one folder, or one
    .yaml config.
    """
    # GUI:
    # corrscope
    # corrscope file.yaml
    # corrscope wildcard/wav/folder ... [--options]
    #
    # CLI:
    # corrscope wildcard/wav/folder ... [--options] --write-cfg file.yaml [--play]
    # corrscope wildcard/wav/folder ... --play
    # corrscope file.yaml --play
    # corrscope file.yaml --write-yaml
    #
    # - You can specify as many wildcards or wav files as you want.
    # - You can only supply one folder, with no files/wildcards.

    show_gui = not any([write, play, render])

    # Gather data for cfg: Config object.
    CfgOrPath = Union[Config, Path]

    cfg_or_path: Union[Config, Path, None] = None
    cfg_dir: Optional[str] = None

    wav_list: List[Path] = []
    for name in files:
        path = Path(name)

        # Windows likes to raise OSError when path contains *
        try:
            is_dir = path.is_dir()
        except OSError:
            is_dir = False
        if is_dir:
            # Add a directory.
            if len(files) > 1:
                # Warning is technically optional, since wav_prefix has been removed.
                raise click.ClickException(
                    f'Cannot supply multiple arguments when providing folder {path}'
                )
            matches = sorted(path.glob('*.wav'))
            wav_list += matches
            break

        elif path.suffix in YAML_EXTS:
            # Load a YAML file to cfg, and skip default_config().
            if len(files) > 1:
                raise click.ClickException(
                    f'Cannot supply multiple arguments when providing config {path}'
                )
            cfg_or_path = path
            cfg_dir = str(path.parent)
            break

        else:
            # Load one or more wav files.
            matches = sorted(Path().glob(name))
            if not matches:
                matches = [path]
                if not path.exists():
                    raise click.ClickException(
                        f'Supplied nonexistent file or wildcard: {path}')
            wav_list += matches

    if not cfg_or_path:
        # cfg and cfg_dir are always initialized together.
        channels = [ChannelConfig(str(wav_path)) for wav_path in wav_list]

        cfg_or_path = default_config(
            master_audio=audio,
            # fps=default,
            channels=channels,
            # width_ms...trigger=default,
            # amplification...render=default,
        )
        cfg_dir = '.'

    assert cfg_or_path is not None
    assert cfg_dir is not None
    if show_gui:

        def command():
            from corrscope import gui
            return gui.gui_main(cast(CfgOrPath, cfg_or_path))

        if profile:
            import cProfile

            # Pycharm can't load CProfile files with dots in the name.
            profile_dump_name = get_profile_dump_name('gui')
            cProfile.runctx('command()', globals(), locals(),
                            profile_dump_name)
        else:
            command()

    else:
        if not files:
            raise click.UsageError('Must specify files or folders to play')

        if isinstance(cfg_or_path, Config):
            cfg = cfg_or_path
            cfg_path = None
        elif isinstance(cfg_or_path, Path):
            cfg = yaml.load(cfg_or_path)
            cfg_path = cfg_or_path
        else:
            assert False, cfg_or_path

        if write:
            write_path = get_path(audio, YAML_NAME)
            yaml.dump(cfg, write_path)

        outputs = []  # type: List[IOutputConfig]

        if play:
            outputs.append(FFplayOutputConfig())

        if render:
            video_path = get_path(cfg_path or audio, VIDEO_NAME)
            outputs.append(FFmpegOutputConfig(video_path))

        if outputs:
            arg = Arguments(cfg_dir=cfg_dir, outputs=outputs)
            command = lambda: CorrScope(cfg, arg).play()
            if profile:
                import cProfile

                # Pycharm can't load CProfile files with dots in the name.
                first_song_name = Path(files[0]).name.split('.')[0]
                profile_dump_name = get_profile_dump_name(first_song_name)
                cProfile.runctx('command()', globals(), locals(),
                                profile_dump_name)
            else:
                try:
                    command()
                except MissingFFmpegError as e:
                    # Tell user how to install ffmpeg (__str__).
                    print(e, file=sys.stderr)