Skip to content

Commit fe265b3

Browse files
committed
feat: read-meta accepts multiple input files
1 parent 97fe421 commit fe265b3

2 files changed

Lines changed: 77 additions & 41 deletions

File tree

src/dirplot/main.py

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -175,50 +175,65 @@ def watch_cmd(
175175

176176
@app.command(name="read-meta")
177177
def read_meta(
178-
file: Path = typer.Argument(..., help="PNG or SVG file to read dirplot metadata from"),
178+
files: list[Path] = typer.Argument(
179+
..., help="PNG or SVG file(s) to read dirplot metadata from"
180+
),
179181
) -> None:
180-
"""Read dirplot metadata embedded in a PNG or SVG file."""
181-
if not file.exists():
182-
typer.echo(f"Error: file not found: {file}", err=True)
183-
raise typer.Exit(1)
184-
185-
suffix = file.suffix.lower()
186-
187-
if suffix == ".png":
188-
from PIL import Image
189-
190-
img = Image.open(file)
191-
info = img.info
192-
meta_keys = {"Date", "Software", "URL", "Python", "OS", "Command"}
193-
found = {k: v for k, v in info.items() if k in meta_keys}
194-
if not found:
195-
typer.echo("No dirplot metadata found in PNG.", err=True)
196-
raise typer.Exit(1)
197-
for k, v in found.items():
198-
typer.echo(f"{k}: {v}")
182+
"""Read dirplot metadata embedded in one or more PNG or SVG files."""
183+
any_error = False
184+
185+
for file in files:
186+
if len(files) > 1:
187+
typer.echo(f"==> {file} <==")
188+
189+
if not file.exists():
190+
typer.echo(f"Error: file not found: {file}", err=True)
191+
any_error = True
192+
continue
193+
194+
suffix = file.suffix.lower()
195+
196+
if suffix == ".png":
197+
from PIL import Image
198+
199+
img = Image.open(file)
200+
info = img.info
201+
meta_keys = {"Date", "Software", "URL", "Python", "OS", "Command"}
202+
found = {k: v for k, v in info.items() if k in meta_keys}
203+
if not found:
204+
typer.echo("No dirplot metadata found in PNG.", err=True)
205+
any_error = True
206+
continue
207+
for k, v in found.items():
208+
typer.echo(f"{k}: {v}")
209+
210+
elif suffix == ".svg":
211+
content = file.read_text(encoding="utf-8")
212+
try:
213+
root = ET.fromstring(content)
214+
except ET.ParseError as exc:
215+
typer.echo(f"Error parsing SVG: {exc}", err=True)
216+
any_error = True
217+
continue
218+
svg_meta: dict[str, str] = {}
219+
for desc in root.iter("{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description"):
220+
for child in desc:
221+
local = child.tag.split("}")[-1] if "}" in child.tag else child.tag
222+
ns_uri = child.tag.split("}")[0].lstrip("{") if "}" in child.tag else ""
223+
if ns_uri == "https://github.com/deeplook/dirplot#" and child.text:
224+
svg_meta[local] = child.text
225+
if not svg_meta:
226+
typer.echo("No dirplot metadata found in SVG.", err=True)
227+
any_error = True
228+
continue
229+
for k, v in svg_meta.items():
230+
typer.echo(f"{k}: {v}")
199231

200-
elif suffix == ".svg":
201-
content = file.read_text(encoding="utf-8")
202-
try:
203-
root = ET.fromstring(content)
204-
except ET.ParseError as exc:
205-
typer.echo(f"Error parsing SVG: {exc}", err=True)
206-
raise typer.Exit(1) from exc
207-
svg_meta: dict[str, str] = {}
208-
for desc in root.iter("{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description"):
209-
for child in desc:
210-
local = child.tag.split("}")[-1] if "}" in child.tag else child.tag
211-
ns_uri = child.tag.split("}")[0].lstrip("{") if "}" in child.tag else ""
212-
if ns_uri == "https://github.com/deeplook/dirplot#" and child.text:
213-
svg_meta[local] = child.text
214-
if not svg_meta:
215-
typer.echo("No dirplot metadata found in SVG.", err=True)
216-
raise typer.Exit(1)
217-
for k, v in svg_meta.items():
218-
typer.echo(f"{k}: {v}")
232+
else:
233+
typer.echo(f"Unsupported file type: {suffix!r}. Expected .png or .svg", err=True)
234+
any_error = True
219235

220-
else:
221-
typer.echo(f"Unsupported file type: {suffix!r}. Expected .png or .svg", err=True)
236+
if any_error:
222237
raise typer.Exit(1)
223238

224239

tests/test_cli.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,27 @@ def test_read_meta_svg_no_metadata(tmp_path: Path) -> None:
163163
assert "No dirplot metadata" in result.output
164164

165165

166+
def test_read_meta_multiple_files(sample_tree: Path, tmp_path: Path) -> None:
167+
out1 = tmp_path / "out1.png"
168+
out2 = tmp_path / "out2.png"
169+
runner.invoke(app, ["map", str(sample_tree), "--no-show", "--output", str(out1)])
170+
runner.invoke(app, ["map", str(sample_tree), "--no-show", "--output", str(out2)])
171+
result = runner.invoke(app, ["read-meta", str(out1), str(out2)])
172+
assert result.exit_code == 0
173+
assert f"==> {out1} <==" in result.output
174+
assert f"==> {out2} <==" in result.output
175+
assert result.output.count("Date:") == 2
176+
177+
178+
def test_read_meta_multiple_files_partial_error(sample_tree: Path, tmp_path: Path) -> None:
179+
out1 = tmp_path / "out1.png"
180+
runner.invoke(app, ["map", str(sample_tree), "--no-show", "--output", str(out1)])
181+
result = runner.invoke(app, ["read-meta", str(out1), "/nonexistent/file.png"])
182+
assert result.exit_code == 1
183+
assert "Date:" in result.output
184+
assert "file not found" in result.output
185+
186+
166187
def test_main_module() -> None:
167188
"""__main__.py delegates to app."""
168189
from dirplot.__main__ import main

0 commit comments

Comments
 (0)