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 """)
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
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)
def test_yaml_object(): @_yaml_loadable class Bar: pass s = yaml.dump(Bar()) assert s == "!Bar {}\n"
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
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)
def test_yaml_object(): @yaml_object(yaml) class Bar: pass s = yaml.dump(Bar()) assert s == "!Bar {}\n"
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 """)
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
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
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 """)
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: '' """)
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 """)
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 """)
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()
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)
def dump_prefs(pref: GlobalPrefs) -> None: with atomic_write(_PREF_PATH, overwrite=True) as f: yaml.dump(pref, f)
def dump_prefs(pref: GlobalPrefs) -> None: yaml.dump(pref, _PREF_PATH)
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)