From 6f07a7422776d0aae4c451b8798dee63f63e551b Mon Sep 17 00:00:00 2001 From: t7929375-eng Date: Sun, 12 Apr 2026 14:03:35 +0200 Subject: [PATCH 1/2] fix: replace mutable default argument in get_all_uids --- gittensor/utils/uids.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gittensor/utils/uids.py b/gittensor/utils/uids.py index bd0f409f..27b4657a 100644 --- a/gittensor/utils/uids.py +++ b/gittensor/utils/uids.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional import bittensor as bt @@ -25,7 +25,7 @@ def check_uid_availability(metagraph: 'bt.Metagraph', uid: int, vpermit_tao_limi return True -def get_all_uids(self, exclude: List[int] = []) -> set[int]: +def get_all_uids(self, exclude: Optional[List[int]] = None) -> set[int]: """Return all eligible miner UIDs for scoring. Args: @@ -35,6 +35,8 @@ def get_all_uids(self, exclude: List[int] = []) -> set[int]: Set of miner UIDs that are serving and within the validator-permit TAO limit. UID ``0`` is always included. """ + if exclude is None: + exclude = [] # Get all available miner UIDs, excluding specified ones available_miner_uids = {uid for uid in range(self.metagraph.n.item()) if uid not in exclude} From 0818ee01d5ca0ce8446e2b62d19a633d3f6bea8e Mon Sep 17 00:00:00 2001 From: t7929375-eng Date: Tue, 14 Apr 2026 10:33:42 +0200 Subject: [PATCH 2/2] test: add unit tests for get_all_uids mutable default fix Covers: - Basic UID enumeration with and without exclusions - UID 0 forced inclusion - Regression: default exclude list not shared across calls - Edge case: empty metagraph --- tests/utils/test_uids.py | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tests/utils/test_uids.py diff --git a/tests/utils/test_uids.py b/tests/utils/test_uids.py new file mode 100644 index 00000000..87be5770 --- /dev/null +++ b/tests/utils/test_uids.py @@ -0,0 +1,58 @@ +# The MIT License (MIT) +# Copyright © 2025 Entrius + +"""Unit tests for gittensor.utils.uids module.""" + +from unittest.mock import Mock + +import pytest + +uids_module = pytest.importorskip('gittensor.utils.uids', reason='Requires gittensor package') +get_all_uids = uids_module.get_all_uids + + +def _make_self(n: int): + """Create a mock object mimicking self.metagraph.n.item().""" + mock = Mock() + mock.metagraph.n.item.return_value = n + return mock + + +class TestGetAllUids: + def test_returns_all_uids_when_no_exclusions(self): + mock_self = _make_self(5) + result = get_all_uids(mock_self) + assert result == {0, 1, 2, 3, 4} + + def test_excludes_specified_uids(self): + mock_self = _make_self(5) + result = get_all_uids(mock_self, exclude=[1, 3]) + assert result == {0, 2, 4} + + def test_uid_zero_always_included_even_when_excluded(self): + mock_self = _make_self(5) + result = get_all_uids(mock_self, exclude=[0]) + assert 0 in result + + def test_default_exclude_not_shared_across_calls(self): + """Regression test: mutable default argument (W0102) must not leak state. + + Before the fix, `exclude: List[int] = []` shared the same list object + across all calls that relied on the default. If any internal or external + code mutated it, subsequent calls would silently inherit the mutation. + """ + mock_self = _make_self(5) + + # First call — default exclude + result1 = get_all_uids(mock_self) + assert result1 == {0, 1, 2, 3, 4} + + # Second call — also default exclude, must return the same result + result2 = get_all_uids(mock_self) + assert result2 == {0, 1, 2, 3, 4} + + def test_empty_metagraph(self): + mock_self = _make_self(0) + result = get_all_uids(mock_self) + # UID 0 is always forced in + assert result == {0}