From ff766b835fa19039043a4cb4ebc37ee03df86cc1 Mon Sep 17 00:00:00 2001 From: Deepseko Date: Wed, 4 Nov 2026 05:34:35 -0500 Subject: [PATCH 1/2] fix(folderparser): take the split from directory names before substrings _decide_split looked for "valid", "train" and "test" anywhere in the lowercased relative path, so a class folder or filename could override the split directory. In a classification dataset, test/train/1.jpg was uploaded to train and train/invalid_ticket.jpg to valid, and unsplit/latest.jpg went to test because of "latest". Nothing warned about it, and the split= argument can't repair a mixed dataset. Take the topmost directory whose whole name is a split (test/, validation/, train_set/), so validation/train/ stays valid while a wrapper like test-project-1/train/ stays train. Otherwise fall back to a match at the start of a word, in the old valid, train, test order, treating a change of case as a word start, so valid_001.jpg, my_test_set/, myTest/ and VOCtest/ keep working and latest/ and invalid/ stop matching. Co-Authored-By: Claude Opus 5.5 --- roboflow/util/folderparser.py | 31 +++++--- tests/util/test_folderparser.py | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 9 deletions(-) diff --git a/roboflow/util/folderparser.py b/roboflow/util/folderparser.py index 047cdaee..c79f127c 100644 --- a/roboflow/util/folderparser.py +++ b/roboflow/util/folderparser.py @@ -10,6 +10,16 @@ IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".avif", ".heic"} ANNOTATION_EXTENSIONS = {".txt", ".json", ".xml", ".csv", ".jsonl"} LABELMAPS_EXTENSIONS = {".labels", ".yaml", ".yml"} +SPLIT_NAMES = ("valid", "train", "test") +# A directory that is a split as a whole: test/, validation/, train_set/. Not my_test/ or test_dataset/. +SPLIT_DIR = re.compile( + r"(?:(?Pvalid(?:ation)?)|(?Ptrain(?:ing)?)|(?Ptest(?:ing)?))(?:[-_ ]?(?:set|data|images))?", + re.IGNORECASE, +) +# A split name at the start of a word, where a change of case also starts one: my_test, myTest, VOCtest, not latest. +SPLIT_WORDS = { + name: re.compile(rf"(?:(? Date: Wed, 4 Nov 2026 06:30:59 -0500 Subject: [PATCH 2/2] docs(changelog): add entry for this fix Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3de8e4fd..dd0275c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,18 @@ All notable changes to this project will be documented in this file. YOLOv10. Existing models remain available for inference, and YOLO dataset formats remain supported ([#572](https://github.com/roboflow/roboflow-python/pull/572)). +### Fixed + +- Directory uploads with `upload_dataset` now take each image's split from the + topmost directory whose whole name is a split (`test/`, `validation/`, + `train_set/`), so a class folder or filename inside it can't override it: + `test/train/1.jpg` goes to test, not train. Without such a directory, a split + name only counts at the start of a word, so `latest/` and `invalid/` no longer + match, while `my_test/`, `myTest/` and `valid_001.jpg` still do. Run-together + names like `mytest/` now go to train, and an unrecognised outer folder loses to + an exact name inside it, so `train_v2/test/` goes to test + ([#576](https://github.com/roboflow/roboflow-python/pull/576)). + ## 1.7.3 ### Added