refactor: replace return-code lists with exceptions and plain return values

This commit is contained in:
randogoth 2026-10-24 21:47:42 +03:00
parent 675e069a51
commit f18d0300b6
7 changed files with 260 additions and 263 deletions

View file

@ -32,7 +32,7 @@ def test_python_keeps_shebang_coding_and_docstring(cwd):
source = '#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n"""Real docstring."""\nprint(__doc__)\n'
(cwd / 's.py').write_text(source)
os.chmod(cwd / 's.py', 0o755)
assert write('s.py', source, '+tools', '#demo', 'a note')[0] == 0
write('s.py', source, '+tools', '#demo', 'a note')
assert (cwd / 's.py').read_text() == (
'#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# otag: +tools #demo a note\n\n'
'"""Real docstring."""\nprint(__doc__)\n'
@ -45,13 +45,13 @@ def test_python_keeps_shebang_coding_and_docstring(cwd):
@pytest.mark.skipif(not shutil.which('perl'), reason='perl not installed')
def test_perl_still_runs(cwd):
assert write('s.pl', 'print "perl ok\\n";\n', '#demo')[0] == 0
write('s.pl', 'print "perl ok\\n";\n', '#demo')
run = subprocess.run(['perl', 's.pl'], capture_output=True, text=True)
assert run.stdout == 'perl ok\n'
def test_php_tag_goes_inside_the_php_block(cwd):
assert write('p.php', '<?php\necho "ok";\n', '#demo')[0] == 0
write('p.php', '<?php\necho "ok";\n', '#demo')
assert (cwd / 'p.php').read_text() == '<?php\n// otag: #demo\n\necho "ok";\n'
@ -62,19 +62,19 @@ def test_php_without_own_opening_line_is_refused(cwd):
def test_go_tag_is_not_a_package_doc_comment(cwd):
assert write('m.go', 'package main\n', '#demo')[0] == 0
write('m.go', 'package main\n', '#demo')
assert (cwd / 'm.go').read_text() == '// otag: #demo\n\npackage main\n'
def test_html_keeps_doctype_first(cwd):
assert write('i.html', '<!DOCTYPE html>\n<html></html>\n', '#demo', 'home page')[0] == 0
write('i.html', '<!DOCTYPE html>\n<html></html>\n', '#demo', 'home page')
assert (cwd / 'i.html').read_text() == '<!DOCTYPE html>\n<!-- otag: #demo home page -->\n\n<html></html>\n'
assert read_back(cwd, 'i.html') == 'i.html --- #demo home page\n'
def test_existing_tag_line_is_replaced_and_crlf_kept(cwd):
source = '// otag: #old\r\n\r\nint main() {}\r\n'
assert write('m.c', source, '#new')[0] == 0
write('m.c', source, '#new')
assert (cwd / 'm.c').read_bytes() == b'// otag: #new\r\n\r\nint main() {}\r\n'

View file

@ -1,7 +1,7 @@
import pytest
import orvanitag.orvani as orvani
from orvanitag.orvani import OrvaniTag
from orvanitag.orvani import OrvaniTag, UnsupportedType
@pytest.fixture
@ -31,7 +31,7 @@ def test_write_changes_only_its_own_keys(cwd):
(cwd / 'post.md').write_text(post)
o = OrvaniTag()
o.add('post.md', '#blog', '#lo-fi', '#new')
assert o.write_metadata('post.md', 'frontmatter')[0] == 0
o.write_metadata('post.md', 'frontmatter')
assert (cwd / 'post.md').read_text() == (
'---\ntitle: Post\n# my comment\ndate: 2024-01-05\ntags: [blog, lo-fi, new]\nlayout: post\n---\nBody text.\n'
)
@ -68,5 +68,6 @@ def test_rst_is_not_handled(cwd):
(cwd / 'doc.rst').write_text('Title\n=====\n')
o = OrvaniTag()
o.add('doc.rst', '#x')
assert o.write_metadata('doc.rst', 'frontmatter')[0] == 4
with pytest.raises(UnsupportedType):
o.write_metadata('doc.rst', 'frontmatter')
assert (cwd / 'doc.rst').read_text() == 'Title\n=====\n'

View file

@ -37,7 +37,7 @@ def test_write_and_read_comment(cwd):
make_wav(cwd / 'a.wav')
o = OrvaniTag()
o.add('a.wav', '+album', '@drums', '#lo-fi', 'first', 'take')
assert o.write_metadata('a.wav', 'id3')[0] == 0
o.write_metadata('a.wav', 'id3')
with taglib.File(cwd / 'a.wav') as song:
assert song.tags['COMMENT'] == ['+album @drums #lo-fi first take']
(cwd / orvani.CONTEXT_FILE).unlink()

View file

@ -3,7 +3,7 @@ import os
import pytest
import orvanitag.orvani as orvani
from orvanitag.orvani import OrvaniTag, DEFAULT_PLUGINS
from orvanitag.orvani import OrvaniTag, DEFAULT_PLUGINS, FormatError, NoEntry, NotFound
@pytest.fixture
@ -77,7 +77,7 @@ def test_clean_keeps_tagged_directories(tree):
o.add('sub', '#folder')
o.add('a.mp3', '#lofi')
(tree / 'a.mp3').unlink()
assert o.clean('', True)[2] == 1
assert o.clean('', True) == 1
assert lines(tree) == ['sub/ --- #folder']
@ -97,13 +97,14 @@ def test_index_is_rebuilt_per_list_call(tree):
def test_move_untagged_file(tree):
assert OrvaniTag().move('a.mp3', 'sub/a.mp3', False, False)[0] == 2
with pytest.raises(NoEntry):
OrvaniTag().move('a.mp3', 'sub/a.mp3', False, False)
def test_move_tagged_file(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
assert o.move('a.mp3', 'sub/', False, True)[0] == 0
o.move('a.mp3', 'sub/', False, True)
assert (tree / 'sub' / 'a.mp3').exists()
assert lines(tree / 'sub') == ['a.mp3 --- #lofi']
assert not (tree / orvani.CONTEXT_FILE).exists()
@ -112,14 +113,14 @@ def test_move_tagged_file(tree):
def test_copy_tags_within_current_directory(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
assert o.move('a.mp3', 'b.mp3', True, False)[0] == 0
o.move('a.mp3', 'b.mp3', True, False)
assert lines(tree) == ['a.mp3 --- #lofi', 'b.mp3 --- #lofi']
def test_rename_within_current_directory(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
assert o.move('a.mp3', 'renamed.mp3', False, True)[0] == 0
o.move('a.mp3', 'renamed.mp3', False, True)
assert not (tree / 'a.mp3').exists()
assert lines(tree) == ['renamed.mp3 --- #lofi']
@ -127,7 +128,7 @@ def test_rename_within_current_directory(tree):
def test_move_into_directory_without_slash(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
assert o.move('a.mp3', 'sub', False, True)[0] == 0
o.move('a.mp3', 'sub', False, True)
assert (tree / 'sub' / 'a.mp3').exists()
assert lines(tree / 'sub') == ['a.mp3 --- #lofi']
assert not (tree / orvani.CONTEXT_FILE).exists()
@ -137,7 +138,7 @@ def test_copy_tags_from_subfolder_to_folder(tree):
(tree / 'other').mkdir()
o = OrvaniTag()
o.add('sub/d.txt', '#doc')
assert o.move('sub/d.txt', 'other/', True, False)[0] == 0
o.move('sub/d.txt', 'other/', True, False)
assert lines(tree) == ['other/ --- #doc']
assert lines(tree / 'sub') == ['d.txt --- #doc']
@ -207,7 +208,7 @@ def test_remove_saves_once(tree, monkeypatch):
saves = []
save = o._OrvaniTag__save
monkeypatch.setattr(o, '_OrvaniTag__save', lambda: saves.append(1) or save())
assert o.remove('a.mp3', '#lofi', '#snare')[0] == 0
o.remove('a.mp3', '#lofi', '#snare')
assert len(saves) == 1
assert lines(tree) == ['a.mp3 --- #keep']
@ -228,11 +229,11 @@ def test_import_untagged_files(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi', 'a note')
o.add('sub/d.txt', '@docs')
assert o.export('context.txt')[0] == 0
o.export('context.txt')
(tree / orvani.CONTEXT_FILE).unlink()
(tree / 'sub' / orvani.CONTEXT_FILE).unlink()
assert OrvaniTag().load('context.txt') == [0, 'context.txt', 2]
assert OrvaniTag().load('context.txt') == 2
assert lines(tree) == ['a.mp3 --- #lofi a note']
assert lines(tree / 'sub') == ['d.txt --- @docs']
@ -241,16 +242,45 @@ def test_import_skips_known_tags_and_empty_lines(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
(tree / 'context.txt').write_text('./a.mp3 --- #lofi #snare\n./b.mp3 --- \n')
assert OrvaniTag().load('context.txt') == [0, 'context.txt', 1]
assert OrvaniTag().load('context.txt') == 1
assert lines(tree) == ['a.mp3 --- #lofi #snare']
def test_import_keeps_hyphenated_tags(tree):
(tree / 'context.txt').write_text('./a.mp3 --- @home-office #lo-fi rock+roll take-2\n')
assert OrvaniTag().load('context.txt')[0] == 0
OrvaniTag().load('context.txt')
assert lines(tree) == ['a.mp3 --- @home-office #lo-fi rock+roll take-2']
def test_duplicate_and_bare_prefixes(tree):
OrvaniTag().add('a.mp3', '#lofi', '#lofi', 'C', '+', 'D')
assert lines(tree) == ['a.mp3 --- #lofi C + D']
def test_move_reports_what_is_missing(tree):
o = OrvaniTag()
with pytest.raises(NotFound, match=r"^Source file x\.mp3 doesn't exist\.$"):
o.move('x.mp3', 'sub/', False, True)
with pytest.raises(NotFound, match=r"^Destination nope/a\.mp3 doesn't exist\.$"):
o.move('a.mp3', 'nope/a.mp3', False, True)
with pytest.raises(NotFound, match=r"^Source file x\.mp3 and destination nope/x don't exist\.$"):
o.move('x.mp3', 'nope/x', False, True)
def test_errors_of_remove_delete_export_and_import(tree):
o = OrvaniTag()
o.add('a.mp3', '#lofi')
with pytest.raises(NoEntry, match='No match found for a.mp3 and #snare'):
o.remove('a.mp3', '#snare')
with pytest.raises(NoEntry):
o.delete('b.mp3')
with pytest.raises(NotFound):
o.add('missing.mp3', '#x')
with pytest.raises(NotFound):
o.load('missing.txt')
(tree / 'bad.txt').write_text('no separator on this line\n')
with pytest.raises(FormatError):
o.load('bad.txt')
o.delete('a.mp3')
with pytest.raises(NoEntry):
o.export('context.txt')

View file

@ -6,7 +6,7 @@ from pypdf import PdfReader, PdfWriter
import orvanitag.orvani as orvani
from orvanitag.cmd import main
from orvanitag.orvani import OrvaniTag
from orvanitag.orvani import OrvaniTag, PluginError, UnsupportedType
DOCX_CORE = (
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
@ -44,7 +44,7 @@ def make_odt(path):
def roundtrip(cwd, name):
o = OrvaniTag()
o.add(name, '#lofi', '+proj', 'R&B', 'C:\\1')
assert o.write_metadata(name, 'office')[0] == 0
o.write_metadata(name, 'office')
(cwd / orvani.CONTEXT_FILE).unlink()
OrvaniTag().read_metadata(name)
assert (cwd / orvani.CONTEXT_FILE).read_text() == f'{name} --- +proj #lofi R&B C:\\1\n'
@ -76,7 +76,7 @@ def test_pdf_roundtrip(cwd):
o = OrvaniTag()
o.add('doc.pdf', '#lofi', '@desk', 'a note')
assert o.write_metadata('doc.pdf', 'pdf')[0] == 0
o.write_metadata('doc.pdf', 'pdf')
meta = PdfReader(cwd / 'doc.pdf').metadata
assert meta['/Title'] == 'T'
@ -130,7 +130,8 @@ def test_write_uses_config_of_the_files_folder(cwd):
writer.write(cwd / 'sub' / 'doc.pdf')
OrvaniTag().add('sub/doc.pdf', '#lofi')
assert OrvaniTag().write_metadata('sub/doc.pdf', 'pdf') == [6, 'pdf']
with pytest.raises(PluginError):
OrvaniTag().write_metadata('sub/doc.pdf', 'pdf')
def test_cmd_scan_defaults_to_current_folder(cwd, monkeypatch):
@ -149,7 +150,8 @@ def test_write_ignores_configured_types_the_plugin_cannot_handle(cwd):
(cwd / orvani.CONFIG_FILE).write_text('[pdf]\ntypes = [".xyz", ".abc", ".pdf"]\n')
(cwd / 'doc.abc').write_text('not a pdf')
OrvaniTag().add('doc.abc', '#lofi')
assert OrvaniTag().write_metadata('doc.abc', 'pdf') == [4, ['.pdf'], 'pdf', True]
with pytest.raises(UnsupportedType, match=r'types: \.pdf\. Its types are set in \.otag\.toml'):
OrvaniTag().write_metadata('doc.abc', 'pdf')
assert (cwd / 'doc.abc').read_text() == 'not a pdf'
@ -160,3 +162,11 @@ def test_pdf_keeps_hyphenated_tags(cwd):
writer.write(cwd / 'doc.pdf')
OrvaniTag().read_metadata('doc.pdf')
assert (cwd / orvani.CONTEXT_FILE).read_text() == 'doc.pdf --- +side-project #lo-fi\n'
def test_cmd_user_errors_exit_with_1(cwd, monkeypatch, capsys):
monkeypatch.setattr(sys, 'argv', ['otag', 'a', 'missing.mp3', '#lofi'])
with pytest.raises(SystemExit) as exit:
main()
assert exit.value.code == 1
assert "File missing.mp3 doesn't exist." in capsys.readouterr().out