예제 #1
0
def test_dump_defaults():
    class Config(DumpableAttrs):
        a: str = "a"
        b: str = "b"

    s = yaml.dump(Config("alpha"))
    assert (s == """\
!Config
a: alpha
""")

    class Config(DumpableAttrs, always_dump="a b"):
        a: str = "a"
        b: str = "b"
        c: str = "c"

    s = yaml.dump(Config())
    assert (s == """\
!Config
a: a
b: b
""")

    class Config(DumpableAttrs, always_dump="*"):
        a: str = "a"
        b: str = "b"

    s = yaml.dump(Config())
    assert (s == """\
!Config
a: a
b: b
""")
예제 #2
0
    def on_action_save(self) -> bool:
        """
        :return: False if user cancels save action.
        """
        if self._cfg_path is None:
            return self.on_action_save_as()

        yaml.dump(self.cfg, self._cfg_path)
        self.any_unsaved = False
        self._update_unsaved_title()
        return True
예제 #3
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)
예제 #4
0
def test_yaml_object():
    @_yaml_loadable
    class Bar:
        pass

    s = yaml.dump(Bar())
    assert s == "!Bar {}\n"
예제 #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
예제 #6
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)
예제 #7
0
def test_yaml_object():
    @yaml_object(yaml)
    class Bar:
        pass

    s = yaml.dump(Bar())
    assert s == "!Bar {}\n"
예제 #8
0
def test_dump_dataclass_order():
    class Config(DumpableAttrs, always_dump="*"):
        a: int = 1
        b: int = 1
        c: int = 1
        d: int = 1
        e: int = 1
        z: int = 1
        y: int = 1
        x: int = 1
        w: int = 1
        v: int = 1

    assert (yaml.dump(Config()) == """\
!Config
a: 1
b: 1
c: 1
d: 1
e: 1
z: 1
y: 1
x: 1
w: 1
v: 1
""")
예제 #9
0
def test_dump_no_line_break():
    """Ensure long paths are not split into multiple lines, at whitespace.
    yaml.width = float("inf")"""
    class Foo(DumpableAttrs):
        long_str: str

    long_str = "x x" * 500
    s = yaml.dump(Foo(long_str))
    assert long_str in s
예제 #10
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
예제 #11
0
def test_dumpable_attrs():
    class Foo(DumpableAttrs):
        foo: int
        bar: int

    s = yaml.dump(Foo(foo=1, bar=2))
    assert (s == """\
!Foo
foo: 1
bar: 2
""")
예제 #12
0
def test_dump_default_factory():
    """ Ensure default factories are not dumped, unless attribute present
    in `always_dump`.

    Based on `attrs.Factory`. """
    class Config(DumpableAttrs):
        # Equivalent to attr.ib(factory=str)
        # See https://www.attrs.org/en/stable/types.html
        a: str = attr.Factory(str)
        b: str = attr.Factory(str)

    s = yaml.dump(Config("alpha"))
    assert (s == """\
!Config
a: alpha
""")

    class Config(DumpableAttrs, always_dump="a b"):
        a: str = attr.Factory(str)
        b: str = attr.Factory(str)
        c: str = attr.Factory(str)

    s = yaml.dump(Config())
    assert (s == """\
!Config
a: ''
b: ''
""")

    class Config(DumpableAttrs, always_dump="*"):
        a: str = attr.Factory(str)
        b: str = attr.Factory(str)

    s = yaml.dump(Config())
    assert (s == """\
!Config
a: ''
b: ''
""")
예제 #13
0
def test_always_dump_inheritance():
    class Config(DumpableAttrs, always_dump="a"):
        a: int = 1
        b: int = 2

    class Special(Config, always_dump="c"):
        c: int = 3
        d: int = 4

    s = yaml.dump(Special())
    assert (s == """\
!Special
a: 1
c: 3
""")
예제 #14
0
def test_exclude_dump():
    """
    Test that the exclude="" parameter can remove fields from always_dump="*".
    """
    class Config(DumpableAttrs, always_dump="*", exclude="b"):
        a: int = 1
        b: int = 2

    s = yaml.dump(Config())
    assert (s == """\
!Config
a: 1
""")

    class Special(Config, exclude="d"):
        c: int = 3
        d: int = 4

    s = yaml.dump(Special())
    assert (s == """\
!Special
a: 1
c: 3
""")
예제 #15
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()
예제 #16
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)
예제 #17
0
def dump_prefs(pref: GlobalPrefs) -> None:
    with atomic_write(_PREF_PATH, overwrite=True) as f:
        yaml.dump(pref, f)
예제 #18
0
def dump_prefs(pref: GlobalPrefs) -> None:
    yaml.dump(pref, _PREF_PATH)
예제 #19
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)