Skip to content

Commit f24564a

Browse files
authored
Merge pull request #60 from dwhswenson/plugins-ordered-set
Preserve plugin load order with `OrderedSet`
2 parents fd5dfb4 + 084eef1 commit f24564a

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

‎paths_cli/tests/test_utils.py‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from paths_cli.utils import *
2+
3+
class TestOrderedSet:
4+
def setup(self):
5+
self.set = OrderedSet(['a', 'b', 'a', 'c', 'd', 'c', 'd'])
6+
7+
def test_len(self):
8+
assert len(self.set) == 4
9+
10+
def test_empty(self):
11+
ordered = OrderedSet()
12+
assert len(ordered) == 0
13+
for _ in ordered:
14+
raise RuntimeError("This should not happen")
15+
16+
def test_order(self):
17+
for truth, beauty in zip("abcd", self.set):
18+
assert truth == beauty
19+
20+
def test_add_existing(self):
21+
assert len(self.set) == 4
22+
self.set.add('a')
23+
assert len(self.set) == 4
24+
assert list(self.set) == ['a', 'b', 'c', 'd']
25+
26+
def test_contains(self):
27+
assert 'a' in self.set
28+
assert not 'q' in self.set
29+
assert 'q' not in self.set
30+
31+
def test_discard(self):
32+
self.set.discard('a')
33+
assert list(self.set) == ['b', 'c', 'd']
34+
35+
def test_discard_add_order(self):
36+
assert list(self.set) == ['a', 'b', 'c', 'd']
37+
self.set.discard('a')
38+
self.set.add('a')
39+
assert list(self.set) == ['b', 'c', 'd', 'a']
40+

‎paths_cli/utils.py‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,49 @@
11
import importlib
22
import pathlib
3+
from collections import abc
34
import click
45
from .plugin_management import FilePluginLoader, NamespacePluginLoader
56

7+
8+
class OrderedSet(abc.MutableSet):
9+
"""Set-like object with ordered iterator (insertion order).
10+
11+
This is used to ensure that only one copy of each plugin is loaded
12+
(set-like behavior) while retaining the insertion order.
13+
14+
Parameters
15+
----------
16+
iterable : Iterable
17+
iterable of objects to initialize with
18+
"""
19+
def __init__(self, iterable=None):
20+
self._set = set([])
21+
self._list = []
22+
if iterable is None:
23+
iterable = []
24+
for item in iterable:
25+
self.add(item)
26+
27+
def __contains__(self, item):
28+
return item in self._set
29+
30+
def __len__(self):
31+
return len(self._set)
32+
33+
def __iter__(self):
34+
return iter(self._list)
35+
36+
def add(self, item):
37+
if item in self._set:
38+
return
39+
self._set.add(item)
40+
self._list.append(item)
41+
42+
def discard(self, item):
43+
self._list.remove(item)
44+
self._set.discard(item)
45+
46+
647
def tag_final_result(result, storage, tag='final_conditions'):
748
"""Save results to a tag in storage.
849
@@ -41,5 +82,5 @@ def get_installed_plugins(default_loader, plugin_types):
4182
NamespacePluginLoader('paths_cli_plugins', plugin_types)
4283

4384
]
44-
plugins = set(sum([loader() for loader in loaders], []))
85+
plugins = OrderedSet(sum([loader() for loader in loaders], []))
4586
return list(plugins)

0 commit comments

Comments
 (0)