From d7b81cf2b54d56cc31503c627723bd430d1d1d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Sat, 19 Sep 2026 00:07:07 +0200 Subject: [PATCH 1/2] Expand missing import paths as glob patterns Keep existing files and directories; otherwise treat the path as a glob so GS1?0130.360 works on the CLI. Co-authored-by: Cursor --- mapillary_tools/utils.py | 33 +++++++++++++++++++++++++++++++++ tests/unit/test_utils.py | 12 ++++++++++++ 2 files changed, 45 insertions(+) diff --git a/mapillary_tools/utils.py b/mapillary_tools/utils.py index b363e70b..de8996a4 100644 --- a/mapillary_tools/utils.py +++ b/mapillary_tools/utils.py @@ -6,12 +6,15 @@ from __future__ import annotations import concurrent.futures +import glob import hashlib import logging import os import typing as T from pathlib import Path +from . import exceptions + # Use "hashlib._Hash" instead of hashlib._Hash because: # AttributeError: module 'hashlib' has no attribute '_Hash' @@ -130,6 +133,36 @@ def find_all_image_samples( return image_samples_by_video_path +def expand_import_paths( + import_paths: T.Iterable[Path], + *, + predicate: T.Callable[[Path], bool] | None = None, + missing: str | None = None, +) -> list[Path]: + """Keep existing files/dirs; otherwise expand each path as a glob pattern.""" + out: list[Path] = [] + for path in import_paths: + if path.is_file() or path.is_dir(): + if predicate is None or predicate(path): + out.append(path) + continue + pattern = os.fspath(path) + matches = [ + Path(p) + for p in glob.glob(pattern, recursive="**" in pattern) + if predicate is None or predicate(Path(p)) + ] + if predicate is None: + matches = [p for p in matches if p.is_file() or p.is_dir()] + matches.sort(key=lambda p: p.name.lower()) + if not matches: + raise exceptions.MapillaryFileNotFoundError( + missing or f"Import file or directory not found: {path}" + ) + out.extend(matches) + return out + + def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]: resolved_paths: set[Path] = set() for p in paths: diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 15b54e81..d0d8bbf1 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -126,6 +126,18 @@ def test_filter_all(tmpdir: py.path.local): ) + +def test_expand_import_paths_glob(tmp_path: Path): + (tmp_path / "GS100130.360").mkdir() + (tmp_path / "GS110130.360").mkdir() + (tmp_path / "GS090130.360").mkdir() + (tmp_path / "skip.txt").write_text("x") + matches = utils.expand_import_paths([tmp_path / "GS1?0130.360"]) + assert [p.name for p in matches] == ["GS100130.360", "GS110130.360"] + one = utils.expand_import_paths([tmp_path / "GS090130.360"]) + assert [p.name for p in one] == ["GS090130.360"] + + class TestSanitizeSerial: """Tests for sanitize_serial function""" From 56c3af1c5336bf770f8d4d691d888dbcdcbe2e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Sun, 20 Sep 2026 22:06:12 +0200 Subject: [PATCH 2/2] ruff format import-glob tests Co-authored-by: Cursor --- tests/unit/test_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index d0d8bbf1..c2a2547b 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -126,7 +126,6 @@ def test_filter_all(tmpdir: py.path.local): ) - def test_expand_import_paths_glob(tmp_path: Path): (tmp_path / "GS100130.360").mkdir() (tmp_path / "GS110130.360").mkdir()