Skip to content

FIX: make combine_list order-deterministic and drop the duplicated workaround - #2614

Merged
Roman Lutz (romanlutz) merged 3 commits into
microsoft:mainfrom
jav1er8:fix/combine-list-deterministic-order
Sep 14, 2026
Merged

Roman Lutz (romanlutz) merged 3 commits into
microsoft:mainfrom
jav1er8:fix/combine-list-deterministic-order

Conversation

@jav1er8

Copy link
Copy Markdown
Contributor

combine_list returns list(set(list1 + list2)), so the order of the result varies between processes under hash randomization:

['illegal', 'bias', 'violence', 'harmful', 'pii']
['pii', 'bias', 'violence', 'illegal', 'harmful']
['harmful', 'illegal', 'bias', 'pii', 'violence']

The codebase already knows

pyrit/models/seeds/seed_dataset.py carries a private _merge_unique whose docstring says:

Used instead of utils.combine_list because the latter goes through set() and is nondeterministic across processes for non-trivial inputs.

And the existing tests compare with set(result) == {...} rather than asserting a result, because there was no order to assert.

Changes

Use dict.fromkeys, which deduplicates while preserving first-occurrence order, and accept None as empty so the function covers everything _merge_unique did. _merge_unique is then removed and its single call site uses combine_list.

combine_list is public API, but moving from an arbitrary order to a defined one cannot break a caller that was already correct.

Tests now assert exact order, including a case that pins first-occurrence ordering across both arguments, and cover the None inputs.

Verification

  • Repeated runs with PYTHONHASHSEED=random now return an identical result
  • pytest tests/unit: 16955 passed, 126 skipped (16953 on main, +2 new tests, nothing broken)
  • ruff format --check, ruff check: clean

…rkaround

combine_list returned list(set(list1 + list2)), so the order of the result varied
between processes under hash randomization:

    ['illegal', 'bias', 'violence', 'harmful', 'pii']
    ['pii', 'bias', 'violence', 'illegal', 'harmful']
    ['harmful', 'illegal', 'bias', 'pii', 'violence']

The codebase already knew. seed_dataset.py carried a private _merge_unique with
this docstring:

    Used instead of ``utils.combine_list`` because the latter goes through
    ``set()`` and is nondeterministic across processes for non-trivial inputs.

and the tests for combine_list compared with set(result) == {...} rather than
asserting a result, because there was no order to assert.

Use dict.fromkeys instead, which deduplicates while preserving first-occurrence
order, and accept None as empty so the function covers what _merge_unique did.
_merge_unique is then removed and its single call site uses combine_list.

combine_list is public API, but going from an arbitrary order to a defined one
cannot break a caller that was already correct.

Tests now assert the exact order, including a case that pins first-occurrence
ordering across both arguments.
@jav1er8

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

Comment thread pyrit/common/utils.py
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep dataset-specific empty metadata normalization at the call site and cover both contracts with regression tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@romanlutz Roman Lutz (romanlutz) 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.

just took care of it myself to speed it up. Thanks a ton for this contribution!

@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Sep 14, 2026
Merged via the queue into microsoft:main with commit b200f8a Sep 14, 2026
51 of 52 checks passed
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.

2 participants