Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions paths_cli/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from paths_cli.utils import *

class TestOrderedSet:
def setup(self):
self.set = OrderedSet(['a', 'b', 'a', 'c', 'd', 'c', 'd'])

def test_len(self):
assert len(self.set) == 4

def test_empty(self):
ordered = OrderedSet()
assert len(ordered) == 0
for _ in ordered:
raise RuntimeError("This should not happen")

def test_order(self):
for truth, beauty in zip("abcd", self.set):
assert truth == beauty

def test_add_existing(self):
assert len(self.set) == 4
self.set.add('a')
assert len(self.set) == 4
assert list(self.set) == ['a', 'b', 'c', 'd']

def test_contains(self):
assert 'a' in self.set
assert not 'q' in self.set
assert 'q' not in self.set

def test_discard(self):
self.set.discard('a')
assert list(self.set) == ['b', 'c', 'd']

def test_discard_add_order(self):
assert list(self.set) == ['a', 'b', 'c', 'd']
self.set.discard('a')
self.set.add('a')
assert list(self.set) == ['b', 'c', 'd', 'a']

43 changes: 42 additions & 1 deletion paths_cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
import importlib
import pathlib
from collections import abc
import click
from .plugin_management import FilePluginLoader, NamespacePluginLoader


class OrderedSet(abc.MutableSet):
"""Set-like object with ordered iterator (insertion order).

This is used to ensure that only one copy of each plugin is loaded
(set-like behavior) while retaining the insertion order.

Parameters
----------
iterable : Iterable
iterable of objects to initialize with
"""
def __init__(self, iterable=None):
self._set = set([])
self._list = []
if iterable is None:
iterable = []
for item in iterable:
self.add(item)

def __contains__(self, item):
return item in self._set

def __len__(self):
return len(self._set)

def __iter__(self):
return iter(self._list)

def add(self, item):
if item in self._set:
return
self._set.add(item)
self._list.append(item)

def discard(self, item):
self._list.remove(item)
self._set.discard(item)


def tag_final_result(result, storage, tag='final_conditions'):
"""Save results to a tag in storage.

Expand Down Expand Up @@ -41,5 +82,5 @@ def get_installed_plugins(default_loader, plugin_types):
NamespacePluginLoader('paths_cli_plugins', plugin_types)

]
plugins = set(sum([loader() for loader in loaders], []))
plugins = OrderedSet(sum([loader() for loader in loaders], []))
return list(plugins)