Skip to content

fix(dataset): raise a clear error when custom dataset_info is not a list#9751

Open
he-yufeng wants to merge 2 commits into
modelscope:mainfrom
he-yufeng:fix/dataset-info-non-list-error
Open

fix(dataset): raise a clear error when custom dataset_info is not a list#9751
he-yufeng wants to merge 2 commits into
modelscope:mainfrom
he-yufeng:fix/dataset-info-non-list-error

Conversation

@he-yufeng

Copy link
Copy Markdown
Contributor

What

register_dataset_info (used by --custom_dataset_info xxx.json) loads the JSON and iterates it with for d_info in dataset_info: _register_d_info(d_info). When a single entry is written as a top-level object {...} instead of the documented one-element list [{...}], for d_info in dataset_info iterates the dict keys (strings), and _preprocess_d_info then runs d_info['preprocess_func'] = ... on a str, raising a cryptic TypeError: 'str' object does not support item assignment with no hint that the JSON shape is wrong.

The built-in dataset_info.json is a list and the docs state it "is a list of metadata for datasets", so a top-level non-list is a user mistake. Dropping the outer [] when copying a single-entry example is an easy one to make.

Fix

Validate that the loaded dataset_info is a list and raise a helpful ValueError otherwise. Valid list inputs are unaffected.

Verification

Reproduced the dispatch logic standalone (no swift deps needed for the type check):

input before after
[{...}] (documented) ok ok
{...} (top-level object) TypeError: 'str' object does not support item assignment ValueError: dataset_info should be a JSON list ...

test_register_dataset_info_rejects_non_list writes a temp .json holding a top-level object and asserts register_dataset_info raises ValueError.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a validation check in register_dataset_info to ensure that dataset_info is a list, raising a ValueError with a clear message if it is not. It also adds a corresponding unit test to verify this behavior. The review feedback highlights two important issues: first, a potential crash in the logging logic of register_dataset_info where .keys() might be called on the list, along with an inaccurate type hint; second, a resource leak in the new test because the temporary file created with delete=False is never cleaned up.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread swift/dataset/register.py Outdated
Comment on lines +106 to +109
if not isinstance(dataset_info, list):
raise ValueError(
f'`dataset_info` should be a JSON list of dataset entries (e.g. `[{{...}}]`), but got '
f'type `{type(dataset_info).__name__}`. Please wrap the entry in a list.')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since dataset_info is now strictly validated to be a list, the logging logic on line 117 will crash with an AttributeError: 'list' object has no attribute 'keys' if log_msg is None and len(dataset_info) >= 5. This is because dataset_info.keys() is called on a list.

To fix this, please update line 117 to slice the list instead of calling .keys(). For example:

log_msg = dataset_info if len(dataset_info) < 5 else dataset_info[:5]

Additionally, the type hint for dataset_info on line 84 (Union[str, List[str], None]) is misleading because dataset_info is actually a list of dictionaries (dataset entries), not a list of strings. It should be updated to Union[str, List[Dict[str, Any]], None].

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 79f3cc5. You're right — once the list validation is in place, list(dataset_info.keys()) (a leftover from when dataset_info was a dict) crashes for 5+ entries whenever log_msg is None (a JSON-list string or a list passed directly). Switched it to dataset_info[:5] and tightened the annotation to Union[str, list, None] since the entries are dicts, not str.

Comment thread tests/general/test_dataset.py Outdated
Comment on lines +91 to +100
with tempfile.NamedTemporaryFile('w', suffix='.json', delete=False, encoding='utf-8') as f:
json.dump({'ms_dataset_id': 'swift/self-cognition'}, f)
path = f.name

try:
register_dataset_info(path)
except ValueError as e:
assert 'list' in str(e)
else:
raise AssertionError('expected ValueError for a non-list dataset_info')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The temporary file created with delete=False is never cleaned up after the test runs. We should wrap the test execution in a try...finally block to ensure the temporary file is deleted, preventing resource leaks in the test environment.

Suggested change
with tempfile.NamedTemporaryFile('w', suffix='.json', delete=False, encoding='utf-8') as f:
json.dump({'ms_dataset_id': 'swift/self-cognition'}, f)
path = f.name
try:
register_dataset_info(path)
except ValueError as e:
assert 'list' in str(e)
else:
raise AssertionError('expected ValueError for a non-list dataset_info')
import os
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as f:
json.dump({"ms_dataset_id": "swift/self-cognition"}, f)
path = f.name
try:
try:
register_dataset_info(path)
except ValueError as e:
assert "list" in str(e)
else:
raise AssertionError("expected ValueError for a non-list dataset_info")
finally:
if os.path.exists(path):
os.remove(path)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 79f3cc5 — wrapped the assertion in an inner try/except/else and added an outer finally: os.remove(path) so the delete=False temp file is always cleaned up.

register_dataset_info loads the --custom_dataset_info JSON and then does
`for d_info in dataset_info: _register_d_info(d_info)`. When a user writes a
single entry as a top-level object `{...}` instead of the documented one-element
list `[{...}]`, `for d_info in dataset_info` iterates the dict keys (strings),
and _preprocess_d_info then does `d_info['preprocess_func'] = ...` on a str,
raising a cryptic `TypeError: 'str' object does not support item assignment`.
The built-in dataset_info.json is a list and the docs state it "is a list of
metadata for datasets", so a non-list is a user mistake that deserves a clear
message.

Validate the loaded dataset_info is a list and raise a helpful ValueError
otherwise. Valid list inputs are unaffected.
@he-yufeng
he-yufeng force-pushed the fix/dataset-info-non-list-error branch from 38d2d21 to 50a99df Compare July 15, 2026 15:21
Address review: register_dataset_info now enforces dataset_info is a list,
so the tail log_msg branch that called dataset_info.keys() (a leftover from
when it was a dict) raised AttributeError for 5+ entries when log_msg is None.
Slice the list instead, tighten the type hint to Union[str, list, None], and
clean up the delete=False temp file in the new test via try/finally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant