Skip to content

Commit 43a9c43

Browse files
committed
Add CallableAction implementation and Action interface
1 parent 52b0bc6 commit 43a9c43

File tree

6 files changed

+118
-9
lines changed

6 files changed

+118
-9
lines changed

contextshell/Action.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from abc import ABC, abstractmethod
2+
from contextshell.NodePath import NodePath
3+
from typing import Dict, Union, Any
4+
5+
6+
class Action(ABC):
7+
@abstractmethod
8+
def __call__(self, target: NodePath, action: NodePath, arguments: Dict[Union[NodePath, int], Any]):
9+
raise NotImplementedError()

contextshell/CallableAction.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from collections import OrderedDict
2+
from typing import Callable, Dict, Union, Any, Tuple, List
3+
4+
from contextshell.Action import Action
5+
from contextshell.NodePath import NodePath
6+
7+
8+
class CallableAction(Action):
9+
def __init__(self, implementation: Callable):
10+
self.implementation = implementation
11+
12+
def __call__(self, target: NodePath, action: NodePath, arguments: Dict[Union[NodePath, int], Any]):
13+
args, kwargs = CallableAction.unpack_argument_tree(arguments)
14+
return self.implementation(target, action, *args, **kwargs)
15+
16+
@staticmethod
17+
def unpack_argument_tree(arguments: Dict[Union[NodePath, int], Any]) -> Tuple[List[Any], Dict[NodePath, Any]]:
18+
args = list()
19+
kwargs = OrderedDict()
20+
for key, value in arguments.items():
21+
if isinstance(key, int):
22+
args.append(value)
23+
else:
24+
kwargs[key] = value
25+
return args, kwargs

contextshell/NodeTreeRoot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from contextshell.Node import Node
22
from contextshell.NodePath import NodePath
33
from contextshell.TreeRoot import TreeRoot
4-
from typing import Callable, Iterable, List
4+
from typing import Callable, List
55

66

77
# TODO: check how implement TemporaryTreeRoot (based on NodeTreeRoot)

tests/unit/CallableActionTests.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import unittest
2+
from unittest.mock import Mock, ANY
3+
from contextshell.NodePath import NodePath as np
4+
from collections import OrderedDict
5+
6+
7+
def create_action(*args, **kwargs):
8+
from contextshell.CallableAction import CallableAction
9+
return CallableAction(*args, **kwargs)
10+
11+
12+
class CallTests(unittest.TestCase):
13+
def test_target_path_is_passed(self):
14+
implementation = Mock()
15+
action = create_action(implementation)
16+
17+
action(np('.target'), np('action'), OrderedDict())
18+
19+
self.assertIn(np('.target'), *implementation.call_args)
20+
21+
def test_action_path_is_passed(self):
22+
implementation = Mock()
23+
action = create_action(implementation)
24+
25+
action(np('.target'), np('action'), OrderedDict())
26+
27+
self.assertIn(np('action'), *implementation.call_args)
28+
29+
def test_arguments_are_unpacked_to_list(self):
30+
implementation = Mock()
31+
action = create_action(implementation)
32+
arguments = OrderedDict([
33+
(0, 'foo'),
34+
(1, 'bar'),
35+
])
36+
37+
action(np('.target'), np('action'), arguments)
38+
39+
implementation.assert_called_with(ANY, ANY, 'foo', 'bar')
40+
41+
def test_arguments_are_unpacked_to_keywords(self):
42+
implementation = Mock()
43+
action = create_action(implementation)
44+
arguments = OrderedDict([
45+
('foo', 'spam'),
46+
('bar', 2),
47+
])
48+
49+
action(np('.target'), np('action'), arguments)
50+
51+
implementation.assert_called_with(ANY, ANY, foo='spam', bar=2)
52+
53+
def test_return_value_is_forwarded(self):
54+
implementation = Mock()
55+
implementation.return_value = 'VALUE'
56+
action = create_action(implementation)
57+
58+
action_result = action(np('.target'), np('action'), OrderedDict())
59+
60+
self.assertEqual('VALUE', action_result)

tests/unit/NodeTreeRootTests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,14 +264,14 @@ class ListActions(unittest.TestCase):
264264
'set',
265265
]
266266

267-
def test_root_actions(self):
267+
def test_default_actions(self):
268268
tree = create_tree()
269269

270270
root_actions = tree.list_actions(np('.'))
271271

272272
self.assertSequenceEqual(ListActions.default_actions, root_actions)
273273

274-
def test_include_parent_actions(self):
274+
def test_node_specific_actions(self):
275275
tree = create_tree()
276276
child_path = np('.child')
277277
tree.create(child_path)

todo.yaml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,27 @@ Issues:
3131
'.remote.hour: set 18' should invoke '.hour: set 18' on the remote host
3232
- Return value forwarding
3333
'.remote.minutes: get' should return value to the local system
34-
- Local action should be able operate on remote tree
35-
'local_action .remote.foo' - local_action should be able to access basic operations of remote '.foo' node
36-
- by basic operations I understand CRUD-like ones
37-
- Remote actions should be able to use local tree values
38-
'.remote.foo: remote_action .bar' - remote action is able to access basic operations of local '.bar' node
34+
- Action arguments can be represented as tree
35+
E.g "foo bar=123 bar.spam=arg" will result in following argument tree
36+
"""
37+
[0] foo
38+
bar 123
39+
bar.spam arg
40+
"""
41+
- How to pass argument tree into action?
42+
- >>> Serialization into ordered dictionary
43+
- Easy to implement
44+
- Backward-compatible with common functions
45+
- Path to the temporary tree
46+
- Requires backend to support temporary trees
47+
- E.g. File backend would require temporary files just for arguments
48+
- New tree type
49+
- Requires architectual change?
50+
- What data is passed to the action on invocation?
51+
- Target path
52+
- Action name
53+
- Arguments
54+
Note: Tree/Root is not passed as it doesn't change between invocations
3955

4056
Filesystem backend:
4157
- Custom types for different files
@@ -46,7 +62,6 @@ Filesystem backend:
4662
Network backend:
4763
- Commands issued are executed on the remote host
4864

49-
5065
Tree backend:
5166
- Serialization to file
5267
- Values

0 commit comments

Comments
 (0)