Skip to content

Commit 5e42ebc

Browse files
committed
fix: Drop a file path listed more than once instead of treating its entries as duplicate keys
A path listed more than once would make every entry of that file a duplicate of itself: under the default fail handling the load failed, and under ignore the first listing won by accident. The builder now drops the repeats, keeps the first occurrence in its place in the order, and logs a warning once at build time. This is the behavior the Node SDK has.
1 parent 6095906 commit 5e42ebc

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

‎ldclient/integrations/overrides.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,14 @@ def build(self, config: DataSourceBuilderConfig) -> OverrideSource: # pylint: d
157157
log.warning("Poll interval %s is below the minimum for the file-based override source; using %s", poll_interval, self.MINIMUM_POLL_INTERVAL)
158158
poll_interval = self.MINIMUM_POLL_INTERVAL
159159

160-
return _FileOverrideSource(abs_file_paths(self.__paths), self.__duplicate_keys_handling, self.__change_detection, poll_interval)
160+
# A path listed more than once would make every entry of that file a duplicate of itself,
161+
# so the repeats are dropped and the first occurrence keeps its place in the order.
162+
all_paths = abs_file_paths(self.__paths)
163+
paths = list(dict.fromkeys(all_paths))
164+
if len(paths) != len(all_paths):
165+
log.warning("The file-based override source lists the same file more than once; the repeats are ignored")
166+
167+
return _FileOverrideSource(paths, self.__duplicate_keys_handling, self.__change_detection, poll_interval)
161168

162169

163170
__all__ = ['ChangeDetection', 'DuplicateKeysHandling', 'FileOverrideSourceBuilder']

‎ldclient/testing/integrations/test_file_override_source.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ def test_builder_resolves_relative_paths():
125125
assert source._paths == [os.path.abspath('relative/overrides.json')]
126126

127127

128+
def test_builder_drops_a_repeated_path_and_warns(caplog):
129+
# Under the default fail handling the same file listed twice would make every one of its keys
130+
# a duplicate of itself. The repeat is ignored and the first occurrence keeps its place.
131+
with caplog.at_level(logging.WARNING):
132+
source = FileOverrideSourceBuilder(['/a.json', '/b.json', '/a.json']).build(SDKConfig('SDK_KEY'))
133+
assert isinstance(source, _FileOverrideSource)
134+
assert source._paths == [os.path.abspath('/a.json'), os.path.abspath('/b.json')]
135+
assert any('more than once' in r.getMessage() for r in caplog.records)
136+
137+
128138
def test_builder_polls_by_default(tmp_path):
129139
path = os.path.join(str(tmp_path), 'overrides.json')
130140
source = FileOverrideSourceBuilder([path]).build(SDKConfig('SDK_KEY'))

0 commit comments

Comments
 (0)