Created
January 5, 2010 19:59
-
-
Save bebraw/269659 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect | |
import imp | |
import os | |
import tempfile | |
from node import TreeNode | |
class File(TreeNode): | |
def __init__(self, path=None): | |
super(File, self).__init__() | |
self.classes = {} | |
self.__init_classes(path) | |
self.__init_structure(path) | |
def __init_classes(self, path): | |
with open(path, 'r') as f: | |
file_content = f.read() | |
# http://docs.python.org/library/tempfile.html#tempfile.mktemp | |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.py') | |
temp_file.write(file_content) | |
temp_file.close() | |
try: | |
module = imp.load_source('', temp_file.name) | |
except Exception, e: | |
print e | |
module_classes = inspect.getmembers(module, inspect.isclass) | |
for name, klass in module_classes: | |
self.classes[name.lower()] = klass | |
os.unlink(temp_file.name) | |
os.unlink(temp_file.name + 'c') | |
def __init_structure(self, path): | |
if not isinstance(path, str) or not path: | |
return | |
current_node = self | |
parts = path.split('/') | |
for part in reversed(parts): | |
current_node.name = part | |
current_node.parent = File() | |
current_node = current_node.parent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
class AbstractNode(object): | |
def _generic_find(self, node_list_name, **kvargs): | |
found_nodes = self._generic_recursion(node_list_name, kvargs, [], []) | |
return self._check_found_nodes(found_nodes) | |
def _generic_recursion(self, node_list_name, search_clauses, found_nodes, | |
visited_nodes): | |
visited_nodes.append(self) | |
nodes = getattr(self, node_list_name) | |
for node in nodes: | |
try: | |
all_match = True | |
for wanted_attribute, wanted_value in search_clauses.items(): | |
attribute = getattr(node, wanted_attribute) | |
if attribute != wanted_value: | |
all_match = False | |
break | |
if all_match: | |
found_nodes.append(node) | |
except AttributeError: | |
pass | |
if node not in visited_nodes: | |
node._generic_recursion(node_list_name, search_clauses, | |
found_nodes, visited_nodes) | |
return found_nodes | |
def _check_found_nodes(self, found_nodes): | |
if len(found_nodes) > 0: | |
return found_nodes[0] if len(found_nodes) == 1 else found_nodes | |
class Node(AbstractNode): | |
def __init__(self): | |
self.children = NodeContainer(self, complementary_items_name='parents') | |
self.parents = NodeContainer(self, complementary_items_name='children') | |
def walk(self): | |
def _walk(nodes): | |
for node in nodes: | |
for child in _walk(node.children): | |
yield child | |
yield node | |
yield self | |
for child_walk_node in _walk(self.children): | |
yield child_walk_node | |
def find_parent_with_attribute(self, attribute): | |
return self._find_first_node_with_attribute('parents', attribute, []) | |
def _find_first_node_with_attribute(self, node_list_name, attribute, | |
visited_nodes): | |
visited_nodes.append(self) | |
nodes = getattr(self, node_list_name) | |
for node in nodes: | |
if hasattr(node, attribute): | |
return node | |
if node not in visited_nodes: | |
return node._find_first_node_with_attribute(node_list_name, | |
attribute, visited_nodes) | |
def find_child(self, **kvargs): | |
return self._generic_find(node_list_name='children', **kvargs) | |
def find_parent(self, **kvargs): | |
return self._generic_find(node_list_name='parents', **kvargs) | |
def find_root(self): | |
return self._generic_find(node_list_name='parents', parents=[]) | |
class TreeNode(AbstractNode): | |
def __init__(self): | |
self.children = NodeContainer(self, complementary_items_name='_parents') | |
# XXX: this is bit of a hack but at least it allows to use generic | |
# search | |
self._parents = NodeContainer(self, complementary_items_name='children') | |
@property | |
def parent(self): | |
return self._parents[0] | |
@parent.setter | |
def parent(self, value): | |
assert len(self._parents) <= 1 | |
if len(self._parents) == 1: | |
self._parents.remove(self._parents[0]) | |
value.children.append(self) | |
def find(self, **kvargs): | |
return self._generic_find(node_list_name='children', **kvargs) | |
class NodeContainer(list): | |
def __init__(self, owner, complementary_items_name): | |
super(NodeContainer, self).__init__() | |
self.owner = owner | |
self.complementary_items_name = complementary_items_name | |
def append(self, *items): | |
for item in items: | |
if item not in self: | |
super(NodeContainer, self).append(item) | |
complementary_items = getattr(item, | |
self.complementary_items_name) | |
complementary_items.append(self.owner) | |
def remove(self, *items): | |
for item in items: | |
if item in self: | |
super(NodeContainer, self).remove(item) | |
complementary_items = getattr(item, | |
self.complementary_items_name) | |
complementary_items.remove(self.owner) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PluginLoader: | |
def load(self, directory): | |
ret = [] | |
for plugin in directory.children: | |
plugin_file = plugin.find(name=plugin.name) | |
plugin_class = plugin_file.classes[plugin.name] | |
ret.append(plugin_class) | |
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from mock import Mock, patch, sentinel | |
from placidity.file import File | |
def mock_with(mock, file_mock): | |
mock.return_value = Mock() | |
mock.return_value.__enter__ = Mock() | |
mock.return_value.__enter__.return_value = file_mock | |
mock.return_value.__exit__ = Mock() | |
def mock_with_read(mock, read_return=sentinel.file_contents): | |
file_mock = Mock() | |
file_mock.read.return_value = read_return | |
mock_with(mock, file_mock) | |
class TestFile: | |
@patch('__builtin__.open') | |
def test_get_file_name(self, open_mock): | |
mock_with_read(open_mock) | |
file = File('/path/to/file') | |
assert file.name == 'file' | |
@patch('__builtin__.open') | |
def test_get_file_parent(self, open_mock): | |
mock_with_read(open_mock) | |
file = File('/path/to/file') | |
assert file.parent.name == 'to' | |
assert file.parent.parent.name == 'path' | |
@patch('__builtin__.open') | |
def test_get_directory_children(self, open_mock): | |
mock_with_read(open_mock) | |
file = File('/path/to/file') | |
directory = file.parent | |
assert directory.children == [file, ] | |
@patch('__builtin__.open') | |
def test_find_by_name(self, open_mock): | |
mock_with_read(open_mock) | |
file = File('/path/to/file') | |
directory = file.parent | |
assert directory.find(name='file') == file | |
@patch('__builtin__.open') | |
def test_load_python_file(self, open_mock): | |
read_return_value = ''' | |
class Bar: flag = True | |
class Foo: flag = False | |
''' | |
mock_with_read(open_mock, read_return_value) | |
file = File(sentinel.filepath) | |
assert 'bar' in file.classes | |
assert file.classes['bar'].flag == True | |
assert 'foo' in file.classes | |
assert file.classes['foo'].flag == False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from placidity.node import Node, TreeNode | |
class TestNode(): | |
def test_append_children_to_node(self): | |
node1, node2 = Node(), Node() | |
node1.children.append(node2) | |
assert node1.children[0] == node2 | |
assert node2.parents[0] == node1 | |
def test_append_parents_to_node(self): | |
node1, node2 = Node(), Node() | |
node1.parents.append(node2) | |
assert node1.parents[0] == node2 | |
assert node2.children[0] == node1 | |
def test_append_same_node_as_child_and_parent(self): | |
node1, node2 = Node(), Node() | |
node1.children.append(node2) | |
node1.parents.append(node2) | |
assert node1.children[0] == node2 | |
assert node1.parents[0] == node2 | |
assert node2.children[0] == node1 | |
assert node2.parents[0] == node1 | |
def test_append_same_node_as_child_multiple_times(self): | |
node1, node2 = Node(), Node() | |
node1.children.append(node2) | |
node1.children.append(node2) | |
node1.children.append(node2) | |
assert node1.children[0] == node2 | |
assert node2.parents[0] == node1 | |
assert len(node1.children) == 1 | |
assert len(node2.parents) == 1 | |
def test_append_same_node_as_parent_multiple_times(self): | |
node1, node2 = Node(), Node() | |
node1.parents.append(node2) | |
node1.parents.append(node2) | |
node1.parents.append(node2) | |
assert node1.parents[0] == node2 | |
assert node2.children[0] == node1 | |
assert len(node1.parents) == 1 | |
assert len(node2.children) == 1 | |
def test_multi_append(self): | |
node1, node2, node3 = Node(), Node(), Node() | |
node1.children.append(node2, node3) | |
assert len(node1.children) == 2 | |
assert node2 in node1.children | |
assert node3 in node1.children | |
def test_remove_child_node(self): | |
node1, node2 = Node(), Node() | |
node1.children.append(node2) | |
node1.children.remove(node2) | |
assert len(node1.children) == 0 | |
assert len(node2.parents) == 0 | |
def test_remove_parent_node(self): | |
node1, node2 = Node(), Node() | |
node1.parents.append(node2) | |
node1.parents.remove(node2) | |
assert len(node1.parents) == 0 | |
assert len(node2.children) == 0 | |
def test_remove_same_node_multiple_times(self): | |
node1, node2 = Node(), Node() | |
node1.parents.append(node2) | |
node1.parents.remove(node2) | |
node1.parents.remove(node2) | |
node1.parents.remove(node2) | |
assert len(node1.parents) == 0 | |
assert len(node2.children) == 0 | |
def test_multi_remove(self): | |
node1, node2, node3 = Node(), Node(), Node() | |
node1.children.append(node2, node3) | |
node1.children.remove(node2, node3) | |
assert len(node1.children) == 0 | |
def test_find_immediate_child_node(self): | |
node1, node2 = Node(), Node() | |
node2.name = 'node to be found' | |
node1.children.append(node2) | |
assert node1.find_child(name='node to be found') == node2 | |
def test_find_child_node_no_results(self): | |
node1 = Node() | |
assert node1.find_child(name='just some name') == None | |
def test_find_child_node_from_node_tree(self): | |
node1 = Node() | |
node1a = Node() | |
node1a1 = Node() | |
node1a1.color = 'blue' | |
node1a2 = Node() | |
node1a2.value = 13 | |
node1b = Node() | |
node1b1 = Node() | |
node1b1.find_me = True | |
node1b1.color = 'blue' | |
node1.children.append(node1a, node1b) | |
node1a.children.append(node1a1, node1a2) | |
node1b.children.append(node1b1) | |
assert node1.find_child(value=13) == node1a2 | |
assert node1.find_child(find_me=True) == node1b1 | |
assert node1.find_child(color='blue') == [node1a1, node1b1] | |
def test_find_immediate_parent_node(self): | |
node1, node2 = Node(), Node() | |
node2.name = 'node to be found' | |
node1.parents.append(node2) | |
assert node1.find_parent(name='node to be found') == node2 | |
def test_find_parent_node_no_results(self): | |
node1 = Node() | |
assert node1.find_parent(name='just some name') == None | |
def test_find_parent_node_from_node_tree(self): | |
node1 = Node() | |
node1a = Node() | |
node1a1 = Node() | |
node1a1.color = 'blue' | |
node1a2 = Node() | |
node1a2.value = 13 | |
node1b = Node() | |
node1b1 = Node() | |
node1b1.find_me = True | |
node1b1.color = 'blue' | |
node1.parents.append(node1a, node1b) | |
node1a.parents.append(node1a1, node1a2) | |
node1b.parents.append(node1b1) | |
assert node1.find_parent(value=13) == node1a2 | |
assert node1.find_parent(find_me=True) == node1b1 | |
assert node1.find_parent(color='blue') == [node1a1, node1b1] | |
assert node1.find_parent(find_me=True, color='blue') == node1b1 | |
def test_find_root(self): | |
node1, node1a, node1b, node1a1 = Node(), Node(), Node(), Node() | |
node1.children.append(node1a, node1b) | |
node1a.children.append(node1a1) | |
assert node1.find_root() == None | |
assert node1a.find_root() == node1 | |
assert node1b.find_root() == node1 | |
assert node1a1.find_root() == node1 | |
def test_cyclic_find(self): | |
node1, node2 = Node(), Node() | |
node1.children.append(node2) | |
node2.children.append(node1) | |
assert node1.find_root() == None | |
assert node2.find_root() == None | |
def test_find_parent_with_value_name(self): | |
node1, node2, node3 = Node(), Node(), Node() | |
node3.attribute_to_find = 'find me' | |
node1.parents.append(node2) | |
node2.parents.append(node3) | |
assert node1.find_parent_with_attribute('attribute_to_find') == node3 | |
def test_walk(self): | |
node1, node2, node3, node4 = Node(), Node(), Node(), Node() | |
node5 = Node() | |
node1.children.append(node2) | |
node1.children.append(node5) | |
node2.children.append(node3) | |
node2.children.append(node4) | |
result = (node1, node3, node4, node2, node5 ) | |
for i, node in enumerate(node1.walk()): | |
assert node == result[i], '%s %s %s' % (i, node, result[i]) | |
class TestTreeNode(): | |
def test_set_parent(self): | |
node1, node2 = TreeNode(), TreeNode() | |
node1.parent = node2 | |
assert node1.parent == node2 | |
assert node2.children == [node1, ] | |
def test_set_parent_twice(self): | |
node1, node2, node3 = TreeNode(), TreeNode(), TreeNode() | |
node1.parent = node2 | |
node1.parent = node3 | |
assert node2.children == [] | |
assert node3.children == [node1, ] | |
def test_find(self): | |
node1, node2, node3 = TreeNode(), TreeNode(), TreeNode() | |
node2.parent = node1 | |
node3.parent = node1 | |
node2.name = 'foo' | |
node3.name = 'bar' | |
assert node1.find(name='foo') == node2 | |
assert node1.find(name='bar') == node3 | |
assert node1.find(name='dummy') == None | |
assert node2.find(name='foo') == None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from mock import Mock | |
from placidity.plugin_loader import PluginLoader | |
class TestPluginLoader: | |
def test_load_plugins(self): | |
class Plugin1: pass | |
class Plugin2: pass | |
plugin_dir = Mock() | |
plugin1_dir = self.create_plugin_dir('plugin1', Plugin1) | |
plugin2_dir = self.create_plugin_dir('plugin2', Plugin2) | |
plugin_dir.children = (plugin1_dir, plugin2_dir) | |
plugin_loader = PluginLoader() | |
assert plugin_loader.load(plugin_dir) == [Plugin1, Plugin2] | |
def create_plugin_dir(self, name, plugin_class): | |
plugin_dir = Mock() | |
plugin_dir.name = name | |
plugin_dir.children = Mock() | |
plugin_file = self.create_plugin_file(name, plugin_class) | |
plugin_dir.find.return_value = plugin_file | |
return plugin_dir | |
def create_plugin_file(self, name, klass): | |
plugin_file = Mock() | |
plugin_file.classes = {name: klass, } | |
return plugin_file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment