You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ariatosca.apache.org by mx...@apache.org on 2017/10/22 07:51:29 UTC

[19/30] incubator-ariatosca git commit: ARIA-324 Refactor ctx proxy access

ARIA-324 Refactor ctx proxy access

Our previous use of "." to delimit nested dict keys was wrong (keys
could have a ".") and inflexible. The new implementation uses subsequent
args to move into the dict. The same format can now be used to access
object attributes.

This commit also changes how to support setting values: we must now use
"=" as the penultimate argument with the new value following.

Calling functions now requires delimiting function arguments via "[" and
"]" arguments. This makes it possible to continue accessing the return
value of a function after the "]".

Also fixed: callables will now "grab" the number of args they need
instead of all remaining args, making it possible to do further
inspection on the returned value from the callable. To allow for this,
kwargs are now expected as the first arg rather than the last.

Relatedly, this commit instruments all parameter fields from all models
and fixes related bugs in the instrumentation implementation.

Furthmore, this commit fixes a bad null check in the ctx client, and
also allows it to retrieve Unicode data.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/a43bc6b1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/a43bc6b1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/a43bc6b1

Branch: refs/heads/new_wagon_setuptools
Commit: a43bc6b1706398dd79eabb2f49b6dafcadc8dfde
Parents: 51e4ed0
Author: Tal Liron <ta...@gmail.com>
Authored: Thu Jul 27 17:58:17 2017 -0500
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue Aug 8 09:32:15 2017 -0500

----------------------------------------------------------------------
 .travis.yml                                     |   2 +-
 aria/orchestrator/context/common.py             |  19 +-
 aria/orchestrator/context/operation.py          |  46 ++---
 .../execution_plugin/ctx_proxy/client.py        |   7 +-
 .../execution_plugin/ctx_proxy/server.py        | 196 +++++++++----------
 aria/orchestrator/workflows/api/task.py         |  13 +-
 aria/storage/collection_instrumentation.py      |  88 ++++-----
 aria/storage/core.py                            |   6 +-
 examples/hello-world/scripts/configure.sh       |  29 ++-
 examples/hello-world/scripts/start.sh           |  38 ++--
 examples/hello-world/scripts/stop.sh            |  17 +-
 tests/end2end/testenv.py                        |  24 ++-
 .../execution_plugin/test_ctx_proxy_server.py   | 150 ++++----------
 .../orchestrator/execution_plugin/test_local.py |  94 ++++-----
 tests/requirements.txt                          |   6 +-
 tests/resources/scripts/test_ssh.sh             |  46 ++---
 tox.ini                                         |   8 +-
 17 files changed, 358 insertions(+), 431 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index edd12d4..c8b7645 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,7 @@ sudo: false
 language: python
 
 python:
-  - "2.7"
+  - '2.7'
 
 env:
   - TOX_ENV=pylint_code

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index f400142..3c5f618 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -38,10 +38,27 @@ class BaseContext(object):
     """
 
     INSTRUMENTATION_FIELDS = (
+        modeling.models.Service.inputs,
+        modeling.models.ServiceTemplate.inputs,
+        modeling.models.Policy.properties,
+        modeling.models.PolicyTemplate.properties,
         modeling.models.Node.attributes,
         modeling.models.Node.properties,
         modeling.models.NodeTemplate.attributes,
-        modeling.models.NodeTemplate.properties
+        modeling.models.NodeTemplate.properties,
+        modeling.models.Group.properties,
+        modeling.models.GroupTemplate.properties,
+        modeling.models.Capability.properties,
+        # TODO ARIA-279: modeling.models.Capability.attributes,
+        modeling.models.CapabilityTemplate.properties,
+        # TODO ARIA-279: modeling.models.CapabilityTemplate.attributes
+        modeling.models.Relationship.properties,
+        modeling.models.Artifact.properties,
+        modeling.models.ArtifactTemplate.properties,
+        modeling.models.Interface.inputs,
+        modeling.models.InterfaceTemplate.inputs,
+        modeling.models.Operation.inputs,
+        modeling.models.OperationTemplate.inputs
     )
 
     class PrefixedLogger(object):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/orchestrator/context/operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py
index 7d5f40c..8613ec3 100644
--- a/aria/orchestrator/context/operation.py
+++ b/aria/orchestrator/context/operation.py
@@ -48,8 +48,7 @@ class BaseOperationContext(common.BaseContext):
     @property
     def task(self):
         """
-        The task in the model storage
-        :return: Task model
+        The task in the model storage.
         """
         # SQLAlchemy prevents from accessing an object which was created on a different thread.
         # So we retrieve the object from the storage if the current thread isn't the same as the
@@ -62,7 +61,7 @@ class BaseOperationContext(common.BaseContext):
     @property
     def plugin_workdir(self):
         """
-        A work directory that is unique to the plugin and the deployment id
+        A work directory that is unique to the plugin and the service ID.
         """
         if self.task.plugin is None:
             return None
@@ -120,20 +119,18 @@ class NodeOperationContext(BaseOperationContext):
     """
 
     @property
-    def node_template(self):
+    def node(self):
         """
-        the node of the current operation
-        :return:
+        The node of the current operation.
         """
-        return self.node.node_template
+        return self.model.node.get(self._actor_id)
 
     @property
-    def node(self):
+    def node_template(self):
         """
-        The node instance of the current operation
-        :return:
+        The node template of the current operation.
         """
-        return self.model.node.get(self._actor_id)
+        return self.node.node_template
 
 
 class RelationshipOperationContext(BaseOperationContext):
@@ -142,41 +139,36 @@ class RelationshipOperationContext(BaseOperationContext):
     """
 
     @property
-    def source_node_template(self):
+    def relationship(self):
         """
-        The source node
-        :return:
+        The relationship instance of the current operation.
         """
-        return self.source_node.node_template
+        return self.model.relationship.get(self._actor_id)
 
     @property
     def source_node(self):
         """
-        The source node instance
-        :return:
+        The relationship source node.
         """
         return self.relationship.source_node
 
     @property
-    def target_node_template(self):
+    def source_node_template(self):
         """
-        The target node
-        :return:
+        The relationship source node template.
         """
-        return self.target_node.node_template
+        return self.source_node.node_template
 
     @property
     def target_node(self):
         """
-        The target node instance
-        :return:
+        The relationship target node.
         """
         return self.relationship.target_node
 
     @property
-    def relationship(self):
+    def target_node_template(self):
         """
-        The relationship instance of the current operation
-        :return:
+        The relationship target node template.
         """
-        return self.model.relationship.get(self._actor_id)
+        return self.target_node.node_template

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/orchestrator/execution_plugin/ctx_proxy/client.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/client.py b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
index 1310c21..84d66f1 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/client.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
@@ -102,9 +102,12 @@ def main(args=None):
     if args.json_output:
         response = json.dumps(response)
     else:
-        if not response:
+        if response is None:
             response = ''
-        response = str(response)
+        try:
+            response = str(response)
+        except UnicodeEncodeError:
+            response = unicode(response).encode('utf8')
     sys.stdout.write(response)
 
 if __name__ == '__main__':

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/orchestrator/execution_plugin/ctx_proxy/server.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/server.py b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
index ca910e0..91b95d9 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/server.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
@@ -17,14 +17,12 @@
 ``ctx`` proxy server implementation.
 """
 
-import collections
 import json
-import re
 import socket
-import threading
-import traceback
 import Queue
 import StringIO
+import threading
+import traceback
 import wsgiref.simple_server
 
 import bottle
@@ -123,9 +121,7 @@ class CtxProxy(object):
     def _process(self, request):
         try:
             with self.ctx.model.instrument(*self.ctx.INSTRUMENTATION_FIELDS):
-                typed_request = json.loads(request)
-                args = typed_request['args']
-                payload = _process_ctx_request(self.ctx, args)
+                payload = _process_request(self.ctx, request)
                 result_type = 'result'
                 if isinstance(payload, exceptions.ScriptException):
                     payload = dict(message=str(payload))
@@ -150,106 +146,94 @@ class CtxProxy(object):
         self.close()
 
 
-def _process_ctx_request(ctx, args):
-    current = ctx
-    num_args = len(args)
-    index = 0
-    while index < num_args:
-        arg = args[index]
-        attr = _desugar_attr(current, arg)
-        if attr:
-            current = getattr(current, attr)
-        elif isinstance(current, collections.MutableMapping):
-            key = arg
-            path_dict = _PathDictAccess(current)
-            if index + 1 == num_args:
-                # read dict prop by path
-                value = path_dict.get(key)
-                current = value
-            elif index + 2 == num_args:
-                # set dict prop by path
-                value = args[index + 1]
-                path_dict.set(key, value)
-                current = None
-            else:
-                raise RuntimeError('Illegal argument while accessing dict')
-            break
-        elif callable(current):
-            kwargs = {}
-            remaining_args = args[index:]
-            if isinstance(remaining_args[-1], collections.MutableMapping):
-                kwargs = remaining_args[-1]
-                remaining_args = remaining_args[:-1]
-            current = current(*remaining_args, **kwargs)
-            break
+class CtxError(RuntimeError):
+    pass
+
+
+class CtxParsingError(CtxError):
+    pass
+
+
+def _process_request(ctx, request):
+    request = json.loads(request)
+    args = request['args']
+    return _process_arguments(ctx, args)
+
+
+def _process_arguments(obj, args):
+    # Modifying?
+    try:
+        # TODO: should there be a way to escape "=" in case it is needed as real argument?
+        equals_index = args.index('=') # raises ValueError if not found
+    except ValueError:
+        equals_index = None
+    if equals_index is not None:
+        if equals_index == 0:
+            raise CtxParsingError('The "=" argument cannot be first')
+        elif equals_index != len(args) - 2:
+            raise CtxParsingError('The "=" argument must be penultimate')
+        modifying = True
+        modifying_key = args[-3]
+        modifying_value = args[-1]
+        args = args[:-3]
+    else:
+        modifying = False
+        modifying_key = None
+        modifying_value = None
+
+    # Parse all arguments
+    while len(args) > 0:
+        obj, args = _process_next_operation(obj, args, modifying)
+
+    if modifying:
+        if hasattr(obj, '__setitem__'):
+            # Modify item value (dict, list, and similar)
+            if isinstance(obj, (list, tuple)):
+                modifying_key = int(modifying_key)
+            obj[modifying_key] = modifying_value
+        elif hasattr(obj, modifying_key):
+            # Modify object attribute
+            setattr(obj, modifying_key, modifying_value)
         else:
-            raise RuntimeError('{0} cannot be processed in {1}'.format(arg, args))
-        index += 1
-    if callable(current):
-        current = current()
-    return current
-
-
-def _desugar_attr(obj, attr):
-    if not isinstance(attr, basestring):
-        return None
-    if hasattr(obj, attr):
-        return attr
-    attr = attr.replace('-', '_')
-    if hasattr(obj, attr):
-        return attr
-    return None
-
-
-class _PathDictAccess(object):
-    pattern = re.compile(r"(.+)\[(\d+)\]")
-
-    def __init__(self, obj):
-        self.obj = obj
-
-    def set(self, prop_path, value):
-        obj, prop_name = self._get_parent_obj_prop_name_by_path(prop_path)
-        obj[prop_name] = value
-
-    def get(self, prop_path):
-        value = self._get_object_by_path(prop_path)
-        return value
-
-    def _get_object_by_path(self, prop_path, fail_on_missing=True):
-        # when setting a nested object, make sure to also set all the
-        # intermediate path objects
-        current = self.obj
-        for prop_segment in prop_path.split('.'):
-            match = self.pattern.match(prop_segment)
-            if match:
-                index = int(match.group(2))
-                property_name = match.group(1)
-                if property_name not in current:
-                    self._raise_illegal(prop_path)
-                if not isinstance(current[property_name], list):
-                    self._raise_illegal(prop_path)
-                current = current[property_name][index]
-            else:
-                if prop_segment not in current:
-                    if fail_on_missing:
-                        self._raise_illegal(prop_path)
-                    else:
-                        current[prop_segment] = {}
-                current = current[prop_segment]
-        return current
-
-    def _get_parent_obj_prop_name_by_path(self, prop_path):
-        split = prop_path.split('.')
-        if len(split) == 1:
-            return self.obj, prop_path
-        parent_path = '.'.join(split[:-1])
-        parent_obj = self._get_object_by_path(parent_path, fail_on_missing=False)
-        prop_name = split[-1]
-        return parent_obj, prop_name
-
-    @staticmethod
-    def _raise_illegal(prop_path):
-        raise RuntimeError('illegal path: {0}'.format(prop_path))
+            raise CtxError('Cannot modify `{0}` of `{1!r}`'.format(modifying_key, obj))
+
+    return obj
+
+
+def _process_next_operation(obj, args, modifying):
+    args = list(args)
+    arg = args.pop(0)
+
+    # Call?
+    if arg == '[':
+        # TODO: should there be a way to escape "[" and "]" in case they are needed as real
+        # arguments?
+        try:
+            closing_index = args.index(']') # raises ValueError if not found
+        except ValueError:
+            raise CtxParsingError('Opening "[" without a closing "]')
+        callable_args = args[:closing_index]
+        args = args[closing_index + 1:]
+        if not callable(obj):
+            raise CtxError('Used "[" and "] on an object that is not callable')
+        return obj(*callable_args), args
+
+    # Attribute?
+    if isinstance(arg, basestring):
+        if hasattr(obj, arg):
+            return getattr(obj, arg), args
+        token_sugared = arg.replace('-', '_')
+        if hasattr(obj, token_sugared):
+            return getattr(obj, token_sugared), args
+
+    # Item? (dict, lists, and similar)
+    if hasattr(obj, '__getitem__'):
+        if modifying and (arg not in obj) and hasattr(obj, '__setitem__'):
+            # Create nested dict
+            obj[arg] = {}
+        return obj[arg], args
+
+    raise CtxParsingError('Cannot parse argument: `{0!r}`'.format(arg))
 
 
 def _get_unused_port():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 4c518fc..ec96d27 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -140,13 +140,18 @@ class OperationTask(BaseTask):
         self.arguments = modeling_utils.merge_parameter_values(arguments,
                                                                operation.arguments,
                                                                model_cls=models.Argument)
-        if getattr(self.actor, 'outbound_relationships', None) is not None:
+
+        actor = self.actor
+        if hasattr(actor, '_wrapped'):
+            # Unwrap instrumented model
+            actor = actor._wrapped
+        if isinstance(actor, models.Node):
             self._context_cls = context.operation.NodeOperationContext
-        elif getattr(self.actor, 'source_node', None) is not None:
+        elif isinstance(actor, models.Relationship):
             self._context_cls = context.operation.RelationshipOperationContext
         else:
-            raise exceptions.TaskCreationException('Could not locate valid context for '
-                                                   '{actor.__class__}'.format(actor=self.actor))
+            raise exceptions.TaskCreationException('Could not create valid context for '
+                                                   '{actor.__class__}'.format(actor=actor))
 
     def __repr__(self):
         return self.name

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/storage/collection_instrumentation.py
----------------------------------------------------------------------
diff --git a/aria/storage/collection_instrumentation.py b/aria/storage/collection_instrumentation.py
index c90cb18..6154e5d 100644
--- a/aria/storage/collection_instrumentation.py
+++ b/aria/storage/collection_instrumentation.py
@@ -146,14 +146,14 @@ class _InstrumentedDict(_InstrumentedCollection, dict):
     def _load(self, dict_=None, **kwargs):
         dict.__init__(
             self,
-            tuple((key, self._raw_value(value)) for key, value in (dict_ or {}).items()),
+            tuple((key, self._raw_value(value)) for key, value in (dict_ or {}).iteritems()),
             **kwargs)
 
     def update(self, dict_=None, **kwargs):
         dict_ = dict_ or {}
-        for key, value in dict_.items():
+        for key, value in dict_.iteritems():
             self[key] = value
-        for key, value in kwargs.items():
+        for key, value in kwargs.iteritems():
             self[key] = value
 
     def __getitem__(self, key):
@@ -202,9 +202,31 @@ class _InstrumentedList(_InstrumentedCollection, list):
 
 class _WrappedBase(object):
 
-    def __init__(self, wrapped, instrumentation):
+    def __init__(self, wrapped, instrumentation, instrumentation_kwargs=None):
+        """
+        :param wrapped: model to be instrumented
+        :param instrumentation: instrumentation dict
+        :param instrumentation_kwargs: arguments for instrumentation class
+        """
         self._wrapped = wrapped
         self._instrumentation = instrumentation
+        self._instrumentation_kwargs = instrumentation_kwargs or {}
+
+    def _wrap(self, value):
+        if value.__class__ in set(class_.class_ for class_ in self._instrumentation):
+            return _create_instrumented_model(
+                value, instrumentation=self._instrumentation, **self._instrumentation_kwargs)
+        # Check that the value is a SQLAlchemy model (it should have metadata) or a collection
+        elif hasattr(value, 'metadata') or isinstance(value, (dict, list)):
+            return _create_wrapped_model(
+                value, instrumentation=self._instrumentation, **self._instrumentation_kwargs)
+        return value
+
+    def __getattr__(self, item):
+        if hasattr(self, '_wrapped'):
+            return self._wrap(getattr(self._wrapped, item))
+        else:
+            super(_WrappedBase, self).__getattribute__(item)
 
 
 class _InstrumentedModel(_WrappedBase):
@@ -213,32 +235,32 @@ class _InstrumentedModel(_WrappedBase):
         """
         The original model.
 
-        :param wrapped: model to be instrumented
         :param mapi: MAPI for the wrapped model
+        :param wrapped: model to be instrumented
+        :param instrumentation: instrumentation dict
+        :param instrumentation_kwargs: arguments for instrumentation class
         """
-        super(_InstrumentedModel, self).__init__(*args, **kwargs)
+        super(_InstrumentedModel, self).__init__(instrumentation_kwargs=dict(mapi=mapi),
+                                                 *args, **kwargs)
         self._mapi = mapi
         self._apply_instrumentation()
 
-    def __getattr__(self, item):
-        return_value = getattr(self._wrapped, item)
-        if isinstance(return_value, self._wrapped.__class__):
-            return _create_instrumented_model(return_value, self._mapi, self._instrumentation)
-        if isinstance(return_value, (list, dict)):
-            return _create_wrapped_model(return_value, self._mapi, self._instrumentation)
-        return return_value
-
     def _apply_instrumentation(self):
         for field in self._instrumentation:
+            if not issubclass(type(self._wrapped), field.parent.class_):
+                # Do not apply if this field is not for our class
+                continue
+
             field_name = field.key
             field_cls = field.mapper.class_
+
             field = getattr(self._wrapped, field_name)
 
-            # Preserve the original value. e.g. original attributes would be located under
-            # _attributes
+            # Preserve the original field, e.g. original "attributes" would be located under
+            # "_attributes"
             setattr(self, '_{0}'.format(field_name), field)
 
-            # set instrumented value
+            # Set instrumented value
             if isinstance(field, dict):
                 instrumentation_cls = _InstrumentedDict
             elif isinstance(field, list):
@@ -247,7 +269,7 @@ class _InstrumentedModel(_WrappedBase):
                 # TODO: raise proper error
                 raise exceptions.StorageError(
                     "ARIA supports instrumentation for dict and list. Field {field} of the "
-                    "class {model} is of {type} type.".format(
+                    "class `{model}` is of type `{type}`.".format(
                         field=field,
                         model=self._wrapped,
                         type=type(field)))
@@ -262,35 +284,13 @@ class _InstrumentedModel(_WrappedBase):
 
 class _WrappedModel(_WrappedBase):
 
-    def __init__(self, instrumentation_kwargs, *args, **kwargs):
-        """
-        :param instrumented_cls: class to be instrumented
-        :param instrumentation_cls: instrumentation cls
-        :param wrapped: currently wrapped instance
-        :param kwargs: passed to the instrumented class
-        """
-        super(_WrappedModel, self).__init__(*args, **kwargs)
-        self._kwargs = instrumentation_kwargs
-
-    def _wrap(self, value):
-        if value.__class__ in (class_.class_ for class_ in self._instrumentation):
-            return _create_instrumented_model(
-                value, instrumentation=self._instrumentation, **self._kwargs)
-        elif hasattr(value, 'metadata') or isinstance(value, (dict, list)):
-            # Basically checks that the value is indeed an sqlmodel (it should have metadata)
-            return _create_wrapped_model(
-                value, instrumentation=self._instrumentation, **self._kwargs)
-        return value
-
-    def __getattr__(self, item):
-        if hasattr(self, '_wrapped'):
-            return self._wrap(getattr(self._wrapped, item))
-        else:
-            super(_WrappedModel, self).__getattribute__(item)
-
     def __getitem__(self, item):
         return self._wrap(self._wrapped[item])
 
+    def __iter__(self):
+        for item in self._wrapped.__iter__():
+            yield self._wrap(item)
+
 
 def _create_instrumented_model(original_model, mapi, instrumentation):
     return type('Instrumented{0}'.format(original_model.__class__.__name__),

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index 2a5745e..7e9b201 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -130,7 +130,7 @@ class ModelStorage(Storage):
         """
         model_name = model_cls.__modelname__
         if model_name in self.registered:
-            self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
+            self.logger.debug('{name} already in storage {self!r}'.format(name=model_name,
                                                                           self=self))
             return
         self.registered[model_name] = self.api(name=model_name,
@@ -151,10 +151,10 @@ class ModelStorage(Storage):
         original_instrumentation = {}
 
         try:
-            for mapi in self.registered.values():
+            for mapi in self.registered.itervalues():
                 original_instrumentation[mapi] = copy.copy(mapi._instrumentation)
                 mapi._instrumentation.extend(instrumentation)
             yield self
         finally:
-            for mapi in self.registered.values():
+            for mapi in self.registered.itervalues():
                 mapi._instrumentation[:] = original_instrumentation[mapi]

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/examples/hello-world/scripts/configure.sh
----------------------------------------------------------------------
diff --git a/examples/hello-world/scripts/configure.sh b/examples/hello-world/scripts/configure.sh
index 9ac26d5..dc7c09f 100755
--- a/examples/hello-world/scripts/configure.sh
+++ b/examples/hello-world/scripts/configure.sh
@@ -16,22 +16,21 @@
 
 set -e
 
-TEMP_DIR="/tmp"
-PYTHON_FILE_SERVER_ROOT=${TEMP_DIR}/python-simple-http-webserver
-if [ -d ${PYTHON_FILE_SERVER_ROOT} ]; then
-	echo "Removing file server root folder ${PYTHON_FILE_SERVER_ROOT}"
-	rm -rf ${PYTHON_FILE_SERVER_ROOT}
-fi
-ctx logger info "Creating HTTP server root directory at ${PYTHON_FILE_SERVER_ROOT}"
-
-mkdir -p ${PYTHON_FILE_SERVER_ROOT}
+TEMP_DIR=/tmp
+PYTHON_FILE_SERVER_ROOT="$TEMP_DIR/python-simple-http-webserver"
+INDEX_PATH=index.html
+IMAGE_PATH=images/aria-logo.png
 
-cd ${PYTHON_FILE_SERVER_ROOT}
+if [ -d "$PYTHON_FILE_SERVER_ROOT" ]; then
+	ctx logger info [ "Removing old web server root folder: $PYTHON_FILE_SERVER_ROOT." ]
+	rm -rf "$PYTHON_FILE_SERVER_ROOT"
+fi
 
-index_path="index.html"
-image_path="images/aria-logo.png"
+ctx logger info [ "Creating web server root folder: $PYTHON_FILE_SERVER_ROOT." ]
 
-ctx logger info "Downloading blueprint resources..."
-ctx download-resource-and-render ${PYTHON_FILE_SERVER_ROOT}/index.html ${index_path}
-ctx download-resource ${PYTHON_FILE_SERVER_ROOT}/aria-logo.png ${image_path}
+mkdir -p "$PYTHON_FILE_SERVER_ROOT"
+cd "$PYTHON_FILE_SERVER_ROOT"
 
+ctx logger info [ "Downloading service template resources..." ]
+ctx download-resource-and-render [ "$PYTHON_FILE_SERVER_ROOT/index.html" "$INDEX_PATH" ]
+ctx download-resource [ "$PYTHON_FILE_SERVER_ROOT/aria-logo.png" "$IMAGE_PATH" ]

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/examples/hello-world/scripts/start.sh
----------------------------------------------------------------------
diff --git a/examples/hello-world/scripts/start.sh b/examples/hello-world/scripts/start.sh
index 010af2c..1525f30 100755
--- a/examples/hello-world/scripts/start.sh
+++ b/examples/hello-world/scripts/start.sh
@@ -16,51 +16,49 @@
 
 set -e
 
-TEMP_DIR="/tmp"
-PYTHON_FILE_SERVER_ROOT=${TEMP_DIR}/python-simple-http-webserver
-PID_FILE="server.pid"
+TEMP_DIR=/tmp
+PYTHON_FILE_SERVER_ROOT="$TEMP_DIR/python-simple-http-webserver"
+PID_FILE=server.pid
+PORT=$(ctx node properties port)
+URL="http://localhost:$PORT"
 
-ctx logger info "Starting HTTP server from ${PYTHON_FILE_SERVER_ROOT}"
+ctx logger info [ "Starting web server at: $PYTHON_FILE_SERVER_ROOT." ]
 
-port=$(ctx node properties port)
-
-cd ${PYTHON_FILE_SERVER_ROOT}
-ctx logger info "Starting SimpleHTTPServer"
-nohup python -m SimpleHTTPServer ${port} > /dev/null 2>&1 &
-echo $! > ${PID_FILE}
-
-ctx logger info "Waiting for server to launch on port ${port}"
-url="http://localhost:${port}"
+cd "$PYTHON_FILE_SERVER_ROOT"
+nohup python -m SimpleHTTPServer "$PORT" > /dev/null 2>&1 &
+echo $! > "$PID_FILE"
 
 server_is_up() {
 	if which wget >/dev/null; then
-		if wget $url >/dev/null; then
+		if wget "$URL" >/dev/null; then
 			return 0
 		fi
 	elif which curl >/dev/null; then
-		if curl $url >/dev/null; then
+		if curl "$URL" >/dev/null; then
 			return 0
 		fi
 	else
-		ctx logger error "Both curl, wget were not found in path"
+		ctx logger error [ "Both curl and wget were not found in path." ]
 		exit 1
 	fi
 	return 1
 }
 
+ctx logger info [ "Waiting for web server to launch on port $PORT..." ]
 STARTED=false
 for i in $(seq 1 15)
 do
 	if server_is_up; then
-		ctx logger info "Server is up."
+		ctx logger info [ "Web server is up." ]
 		STARTED=true
     	break
 	else
-		ctx logger info "Server not up. waiting 1 second."
+		ctx logger info [ "Web server not up. waiting 1 second." ]
 		sleep 1
 	fi
 done
-if [ ${STARTED} = false ]; then
-	ctx logger error "Failed starting web server in 15 seconds."
+
+if [ "$STARTED" = false ]; then
+	ctx logger error [ "Web server did not start within 15 seconds." ]
 	exit 1
 fi

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/examples/hello-world/scripts/stop.sh
----------------------------------------------------------------------
diff --git a/examples/hello-world/scripts/stop.sh b/examples/hello-world/scripts/stop.sh
index e393dbf..be4d68e 100755
--- a/examples/hello-world/scripts/stop.sh
+++ b/examples/hello-world/scripts/stop.sh
@@ -16,14 +16,13 @@
 
 set -e
 
-TEMP_DIR="/tmp"
-PYTHON_FILE_SERVER_ROOT=${TEMP_DIR}/python-simple-http-webserver
-PID_FILE="server.pid"
+TEMP_DIR=/tmp
+PYTHON_FILE_SERVER_ROOT="${TEMP_DIR}/python-simple-http-webserver"
+PID_FILE=server.pid
+PID=$(cat "$PYTHON_FILE_SERVER_ROOT/$PID_FILE")
 
-PID=`cat ${PYTHON_FILE_SERVER_ROOT}/${PID_FILE}`
+ctx logger info [ "Shutting down web server, pid = ${PID}." ]
+kill -9 "$PID" || exit $?
 
-ctx logger info "Shutting down file server. pid = ${PID}"
-kill -9 ${PID} || exit $?
-
-ctx logger info "Deleting file server root directory (${PYTHON_FILE_SERVER_ROOT})"
-rm -rf ${PYTHON_FILE_SERVER_ROOT}
+ctx logger info [ "Removing web server root folder: $PYTHON_FILE_SERVER_ROOT." ]
+rm -rf "$PYTHON_FILE_SERVER_ROOT"

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tests/end2end/testenv.py
----------------------------------------------------------------------
diff --git a/tests/end2end/testenv.py b/tests/end2end/testenv.py
index 85714e5..43ec274 100644
--- a/tests/end2end/testenv.py
+++ b/tests/end2end/testenv.py
@@ -24,7 +24,7 @@ def testenv(tmpdir, request, monkeypatch):
     test_name = request.node.name
     workdir = str(tmpdir)
 
-    # setting the workdir environment variable for the CLI to work with
+    # Setting the workdir environment variable for the CLI
     monkeypatch.setenv('ARIA_WORKDIR', workdir)
     return TestEnvironment(workdir, test_name)
 
@@ -70,27 +70,33 @@ class TestEnvironment(object):
         assert len(self.model_storage.log.list()) == 0
 
     def _get_cli(self):
-        cli = sh.aria.bake('-vvv', _out=sys.stdout.write, _err=sys.stderr.write)
+        cli = sh.aria.bake('-vvv', _out=sys.stdout, _err=sys.stderr)
 
-        # the `sh` library supports underscore-dash auto-replacement for commands and option flags
-        # yet not for subcommands (e.g. `aria service-templates`); The following class fixes this.
         class PatchedCli(object):
+            """
+            The ``sh`` library supports underscore-dash auto-replacement for commands and option
+            flags yet not for subcommands (e.g. ``aria service-templates``). This class fixes this.
+            """
             def __getattr__(self, attr):
                 if '_' in attr:
                     return cli.bake(attr.replace('_', '-'))
                 return getattr(cli, attr)
 
             def __call__(self, *args, **kwargs):
-                # this is to support the `aria` command itself (e.g. `aria --version` calls)
+                """
+                This is to support the ``aria`` command itself (e.g. ``aria --version`` calls).
+                """
                 return cli(*args, **kwargs)
 
         return PatchedCli()
 
     def _get_aria_env(self):
-        # a somewhat hackish but most simple way of acquiring environment context such as
-        # the model storage, resource storage etc.
-        # note that the `ARIA_WORKDIR` environment variable must be exported before the import
-        # below is used, as the import itself will initialize the `.aria` directory.
+        """
+        A somewhat hacky but most simple way of acquiring environment context such as the model
+        storage, resource storage, etc. Note that the ``ARIA_WORKDIR`` environment variable must be
+        exported before the import below is used, as the import itself will initialize the ``.aria``
+        directory.
+        """
         from aria.cli import env as cli_env
         reload(cli_env)  # reloading the module in-between tests
         return cli_env.env

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tests/orchestrator/execution_plugin/test_ctx_proxy_server.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_ctx_proxy_server.py b/tests/orchestrator/execution_plugin/test_ctx_proxy_server.py
index 7ab1bdb..94b7409 100644
--- a/tests/orchestrator/execution_plugin/test_ctx_proxy_server.py
+++ b/tests/orchestrator/execution_plugin/test_ctx_proxy_server.py
@@ -39,53 +39,48 @@ class TestCtxProxy(object):
         assert response == 'value1'
 
     def test_dict_prop_access_get_key_nested(self, server):
-        response = self.request(server, 'node', 'properties', 'prop2.nested_prop1')
+        response = self.request(server, 'node', 'properties', 'prop2', 'nested_prop1')
         assert response == 'nested_value1'
 
     def test_dict_prop_access_get_with_list_index(self, server):
-        response = self.request(server, 'node', 'properties', 'prop3[2].value')
+        response = self.request(server, 'node', 'properties', 'prop3', 2, 'value')
         assert response == 'value_2'
 
     def test_dict_prop_access_set(self, server, ctx):
-        self.request(server, 'node', 'properties', 'prop4.key', 'new_value')
-        self.request(server, 'node', 'properties', 'prop3[2].value', 'new_value_2')
-        self.request(server, 'node', 'properties', 'prop4.some.new.path',
+        self.request(server, 'node', 'properties', 'prop4', 'key', '=', 'new_value')
+        self.request(server, 'node', 'properties', 'prop3', 2, 'value', '=', 'new_value_2')
+        self.request(server, 'node', 'properties', 'prop4', 'some', 'new', 'path', '=',
                      'some_new_value')
         assert ctx.node.properties['prop4']['key'] == 'new_value'
         assert ctx.node.properties['prop3'][2]['value'] == 'new_value_2'
         assert ctx.node.properties['prop4']['some']['new']['path'] == 'some_new_value'
 
+    def test_dict_prop_access_set_with_list_index(self, server, ctx):
+        self.request(server, 'node', 'properties', 'prop3', 2, '=', 'new_value')
+        assert ctx.node.properties['prop3'][2] == 'new_value'
+
     def test_illegal_dict_access(self, server):
-        self.request(server, 'node', 'properties', 'prop4.key', 'new_value')
+        self.request(server, 'node', 'properties', 'prop4', 'key', '=', 'new_value')
         with pytest.raises(RuntimeError):
-            self.request(server, 'node', 'properties', 'prop4.key', 'new_value', 'what')
+            self.request(server, 'node', 'properties', 'prop4', 'key', '=', 'new_value', 'what')
 
     def test_method_invocation(self, server):
-        args = ['arg1', 'arg2', 'arg3']
+        args = ['[', 'arg1', 'arg2', 'arg3', ']']
         response_args = self.request(server, 'stub-method', *args)
-        assert response_args == args
+        assert response_args == args[1:-1]
 
     def test_method_invocation_no_args(self, server):
-        response = self.request(server, 'stub-method')
+        response = self.request(server, 'stub-method', '[', ']')
         assert response == []
 
-    def test_method_invocation_kwargs(self, server):
-        arg1 = 'arg1'
-        arg2 = 'arg2'
-        arg4 = 'arg4_override'
-        arg5 = 'arg5'
-        kwargs = dict(
-            arg4=arg4,
-            arg5=arg5)
-        response = self.request(server, 'stub_args', arg1, arg2, kwargs)
-        assert response == dict(
-            arg1=arg1,
-            arg2=arg2,
-            arg3='arg3',
-            arg4=arg4,
-            args=[],
-            kwargs=dict(
-                arg5=arg5))
+    def test_method_return_value(self, server, ctx):
+        response_args = self.request(server, 'node', 'get_prop', '[', 'prop2', ']', 'nested_prop1')
+        assert response_args == 'nested_value1'
+
+    def test_method_return_value_set(self, server, ctx):
+        self.request(
+            server, 'node', 'get_prop', '[', 'prop2', ']', 'nested_prop1', '=', 'new_value')
+        assert ctx.node.properties['prop2']['nested_prop1'] == 'new_value'
 
     def test_empty_return_value(self, server):
         response = self.request(server, 'stub_none')
@@ -94,7 +89,7 @@ class TestCtxProxy(object):
     def test_client_request_timeout(self, server):
         with pytest.raises(IOError):
             ctx_proxy.client._client_request(server.socket_url,
-                                             args=['stub-sleep', '0.5'],
+                                             args=['stub-sleep', '[', '0.5', ']'],
                                              timeout=0.1)
 
     def test_processing_exception(self, server):
@@ -106,9 +101,9 @@ class TestCtxProxy(object):
             self.request(server, 'logger')
 
     def test_no_string_arg(self, server):
-        args = ['stub_method', 1, 2]
-        response = self.request(server, *args)
-        assert response == args[1:]
+        args = ['[', 1, 2, ']']
+        response = self.request(server, 'stub_method', *args)
+        assert response == args[1:-1]
 
     class StubAttribute(object):
         some_property = 'some_value'
@@ -117,6 +112,9 @@ class TestCtxProxy(object):
         def __init__(self, properties):
             self.properties = properties
 
+        def get_prop(self, name):
+            return self.properties[name]
+
     @staticmethod
     def stub_method(*args):
         return args
@@ -155,11 +153,11 @@ class TestCtxProxy(object):
             }
         }
         ctx.stub_none = None
-        ctx.stub_method = self.stub_method
-        ctx.stub_sleep = self.stub_sleep
-        ctx.stub_args = self.stub_args
-        ctx.stub_attr = self.StubAttribute()
-        ctx.node = self.NodeAttribute(properties)
+        ctx.stub_method = TestCtxProxy.stub_method
+        ctx.stub_sleep = TestCtxProxy.stub_sleep
+        ctx.stub_args = TestCtxProxy.stub_args
+        ctx.stub_attr = TestCtxProxy.StubAttribute()
+        ctx.node = TestCtxProxy.NodeAttribute(properties)
         ctx.model = mocker.MagicMock()
         return ctx
 
@@ -236,10 +234,10 @@ class TestArgumentParsing(object):
         self.assert_valid_output({'key': 1},
                                  "{'key': 1}",
                                  '{"key": 1}')
-        self.assert_valid_output(False, '', 'false')
+        self.assert_valid_output(False, 'False', 'false')
         self.assert_valid_output(True, 'True', 'true')
-        self.assert_valid_output([], '', '[]')
-        self.assert_valid_output({}, '', '{}')
+        self.assert_valid_output([], '[]', '[]')
+        self.assert_valid_output({}, '{}', '{}')
 
     def assert_valid_output(self, response, ex_typed_output, ex_json_output):
         self.mock_response = response
@@ -285,77 +283,3 @@ class TestCtxEntryPoint(object):
                              stderr=subprocess.PIPE)
         p.communicate()
         assert not p.wait()
-
-
-class TestPathDictAccess(object):
-    def test_simple_set(self):
-        obj = {}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        path_dict.set('foo', 42)
-        assert obj == {'foo': 42}
-
-    def test_nested_set(self):
-        obj = {'foo': {}}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        path_dict.set('foo.bar', 42)
-        assert obj == {'foo': {'bar': 42}}
-
-    def test_set_index(self):
-        obj = {'foo': [None, {'bar': 0}]}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        path_dict.set('foo[1].bar', 42)
-        assert obj == {'foo': [None, {'bar': 42}]}
-
-    def test_set_nonexistent_parent(self):
-        obj = {}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        path_dict.set('foo.bar', 42)
-        assert obj == {'foo': {'bar': 42}}
-
-    def test_set_nonexistent_parent_nested(self):
-        obj = {}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        path_dict.set('foo.bar.baz', 42)
-        assert obj == {'foo': {'bar': {'baz': 42}}}
-
-    def test_simple_get(self):
-        obj = {'foo': 42}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        result = path_dict.get('foo')
-        assert result == 42
-
-    def test_nested_get(self):
-        obj = {'foo': {'bar': 42}}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        result = path_dict.get('foo.bar')
-        assert result == 42
-
-    def test_nested_get_shadows_dotted_name(self):
-        obj = {'foo': {'bar': 42}, 'foo.bar': 58}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        result = path_dict.get('foo.bar')
-        assert result == 42
-
-    def test_index_get(self):
-        obj = {'foo': [0, 1]}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        result = path_dict.get('foo[1]')
-        assert result == 1
-
-    def test_get_nonexistent(self):
-        obj = {}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        with pytest.raises(RuntimeError):
-            path_dict.get('foo')
-
-    def test_get_by_index_not_list(self):
-        obj = {'foo': {0: 'not-list'}}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        with pytest.raises(RuntimeError):
-            path_dict.get('foo[0]')
-
-    def test_get_by_index_nonexistent_parent(self):
-        obj = {}
-        path_dict = ctx_proxy.server._PathDictAccess(obj)
-        with pytest.raises(RuntimeError):
-            path_dict.get('foo[1]')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tests/orchestrator/execution_plugin/test_local.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_local.py b/tests/orchestrator/execution_plugin/test_local.py
index e64e998..7f33318 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -43,10 +43,10 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes map.key value
+            ctx node attributes map key = value
             ''',
             windows_script='''
-            ctx node attributes map.key value
+            ctx node attributes map key = value
         ''')
         props = self._run(
             executor, workflow_context,
@@ -57,12 +57,12 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes map.key1 $key1
-            ctx node attributes map.key2 $key2
+            ctx node attributes map key1 = "$key1"
+            ctx node attributes map key2 = "$key2"
             ''',
             windows_script='''
-            ctx node attributes map.key1 %key1%
-            ctx node attributes map.key2 %key2%
+            ctx node attributes map key1 = %key1%
+            ctx node attributes map key2 = %key2%
         ''')
         props = self._run(
             executor, workflow_context,
@@ -81,10 +81,10 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes map.cwd $PWD
+            ctx node attributes map cwd = "$PWD"
             ''',
             windows_script='''
-            ctx node attributes map.cwd %CD%
+            ctx node attributes map cwd = %CD%
             ''')
         tmpdir = str(tmpdir)
         props = self._run(
@@ -97,7 +97,7 @@ class TestLocalRunScript(object):
         assert p_map['cwd'] == tmpdir
 
     def test_process_command_prefix(self, executor, workflow_context, tmpdir):
-        use_ctx = 'ctx node attributes map.key value'
+        use_ctx = 'ctx node attributes map key = value'
         python_script = ['import subprocess',
                          'subprocess.Popen("{0}".split(' ')).communicate()[0]'.format(use_ctx)]
         python_script = '\n'.join(python_script)
@@ -121,12 +121,12 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes map.arg1 "$1"
-            ctx node attributes map.arg2 $2
+            ctx node attributes map arg1 = "$1"
+            ctx node attributes map arg2 = "$2"
             ''',
             windows_script='''
-            ctx node attributes map.arg1 %1
-            ctx node attributes map.arg2 %2
+            ctx node attributes map arg1 = %1
+            ctx node attributes map arg2 = %2
             ''')
         props = self._run(
             executor, workflow_context,
@@ -149,12 +149,12 @@ class TestLocalRunScript(object):
             tmpdir,
             linux_script='''#! /bin/bash -e
             echo 123123
-            command_that_does_not_exist
+            command_that_does_not_exist [ ]
             ''',
             windows_script='''
             @echo off
             echo 123123
-            command_that_does_not_exist
+            command_that_does_not_exist [ ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -209,10 +209,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes key "${input_as_env_var}"
+            ctx node attributes key = "${input_as_env_var}"
             ''',
             windows_script='''
-            ctx node attributes key "%input_as_env_var%"
+            ctx node attributes key = "%input_as_env_var%"
         ''')
         props = self._run(
             executor, workflow_context,
@@ -228,10 +228,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node attributes key "${input_as_env_var}"
+            ctx node attributes key = "${input_as_env_var}"
             ''',
             windows_script='''
-            ctx node attributes key "%input_as_env_var%"
+            ctx node attributes key = "%input_as_env_var%"
         ''')
 
         props = self._run(
@@ -268,10 +268,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx -j instance attributes nonexistent
+            ctx -j node attributes nonexistent
             ''',
             windows_script='''
-            ctx -j instance attributes nonexistent
+            ctx -j node attributes nonexistent
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -285,10 +285,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx task abort abort-message
+            ctx task abort [ abort-message ]
             ''',
             windows_script='''
-            ctx task abort abort-message
+            ctx task abort [ abort-message ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -300,10 +300,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx task retry retry-message
+            ctx task retry [ retry-message ]
             ''',
             windows_script='''
-            ctx task retry retry-message
+            ctx task retry [ retry-message ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -315,10 +315,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx task retry retry-message @100
+            ctx task retry [ retry-message @100 ]
             ''',
             windows_script='''
-            ctx task retry retry-message @100
+            ctx task retry [ retry-message @100 ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -331,12 +331,12 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash
-            ctx task retry retry-message
-            ctx task abort should-raise-a-runtime-error
+            ctx task retry [ retry-message ]
+            ctx task abort [ should-raise-a-runtime-error ]
             ''',
             windows_script='''
-            ctx task retry retry-message
-            ctx task abort should-raise-a-runtime-error
+            ctx task retry [ retry-message ]
+            ctx task abort [ should-raise-a-runtime-error ]
         ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -348,12 +348,12 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash
-            ctx task abort abort-message
-            ctx task retry should-raise-a-runtime-error
+            ctx task abort [ abort-message ]
+            ctx task retry [ should-raise-a-runtime-error ]
             ''',
             windows_script='''
-            ctx task abort abort-message
-            ctx task retry should-raise-a-runtime-error
+            ctx task abort [ abort-message ]
+            ctx task retry [ should-raise-a-runtime-error ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -365,12 +365,12 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash
-            ctx task abort abort-message
-            ctx task abort should-raise-a-runtime-error
+            ctx task abort [ abort-message ]
+            ctx task abort [ should-raise-a-runtime-error ]
             ''',
             windows_script='''
-            ctx task abort abort-message
-            ctx task abort should-raise-a-runtime-error
+            ctx task abort [ abort-message ]
+            ctx task abort [ should-raise-a-runtime-error ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -382,12 +382,12 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash
-            ctx task retry retry-message
-            ctx task retry should-raise-a-runtime-error
+            ctx task retry [ retry-message ]
+            ctx task retry [ should-raise-a-runtime-error ]
             ''',
             windows_script='''
-            ctx task retry retry-message
-            ctx task retry should-raise-a-runtime-error
+            ctx task retry [ retry-message ]
+            ctx task retry [ should-raise-a-runtime-error ]
             ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -401,11 +401,11 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx task retry '{0}' 2> {1}
+            ctx task retry [ "{0}" ] 2> {1}
             echo should-not-run > {1}
             '''.format(message, log_path),
             windows_script='''
-            ctx task retry "{0}" 2> {1}
+            ctx task retry [ "{0}" ] 2> {1}
             if %errorlevel% neq 0 exit /b %errorlevel%
             echo should-not-run > {1}
             '''.format(message, log_path))
@@ -421,11 +421,11 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx task abort '{0}' 2> {1}
+            ctx task abort [ "{0}" ] 2> {1}
             echo should-not-run > {1}
             '''.format(message, log_path),
             windows_script='''
-            ctx task abort "{0}" 2> {1}
+            ctx task abort [ "{0}" ] 2> {1}
             if %errorlevel% neq 0 exit /b %errorlevel%
             echo should-not-run > {1}
             '''.format(message, log_path))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tests/requirements.txt
----------------------------------------------------------------------
diff --git a/tests/requirements.txt b/tests/requirements.txt
index d86750b..56a7bf5 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -16,7 +16,7 @@ sh==1.12.14
 psutil==5.2.2
 mock==2.0.0
 pylint==1.6.5
-pytest==3.1.3
+pytest==3.2.0
 pytest-cov==2.5.1
-pytest-mock==1.6.0
-pytest-xdist==1.18.1
+pytest-mock==1.6.2
+pytest-xdist==1.18.2

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tests/resources/scripts/test_ssh.sh
----------------------------------------------------------------------
diff --git a/tests/resources/scripts/test_ssh.sh b/tests/resources/scripts/test_ssh.sh
index 4261d7e..1c35370 100644
--- a/tests/resources/scripts/test_ssh.sh
+++ b/tests/resources/scripts/test_ssh.sh
@@ -19,7 +19,7 @@ set -u
 set -e
 
 test_run_script_basic() {
-    ctx node attributes test_value $test_value
+    ctx node attributes test_value = "$test_value"
 }
 
 test_run_script_as_sudo() {
@@ -27,7 +27,7 @@ test_run_script_as_sudo() {
 }
 
 test_run_script_default_base_dir() {
-    ctx node attributes work_dir $PWD
+    ctx node attributes work_dir = "$PWD"
 }
 
 test_run_script_with_hide() {
@@ -35,44 +35,44 @@ test_run_script_with_hide() {
 }
 
 test_run_script_process_config() {
-    ctx node attributes env_value $test_value_env
-    ctx node attributes bash_version $BASH_VERSION
-    ctx node attributes arg1_value $1
-    ctx node attributes arg2_value $2
-    ctx node attributes cwd $PWD
-    ctx node attributes ctx_path $(which ctx)
+    ctx node attributes env_value = "$test_value_env"
+    ctx node attributes bash_version = "$BASH_VERSION"
+    ctx node attributes arg1_value = "$1"
+    ctx node attributes arg2_value = "$2"
+    ctx node attributes cwd = "$PWD"
+    ctx node attributes ctx_path = "$(which ctx)"
 }
 
 test_run_script_command_prefix() {
-    ctx node attributes dollar_dash $-
+    ctx node attributes dollar_dash = $-
 }
 
 test_run_script_reuse_existing_ctx_1() {
-    ctx node attributes test_value1 $test_value1
+    ctx node attributes test_value1 = "$test_value1"
 }
 
 test_run_script_reuse_existing_ctx_2() {
-    ctx node attributes test_value2 $test_value2
+    ctx node attributes test_value2 = "$test_value2"
 }
 
 test_run_script_download_resource_plain() {
-    local destination=$(mktemp)
-    ctx download-resource ${destination} test_resource
-    ctx node attributes test_value "$(cat ${destination})"
+    local DESTINATION=$(mktemp)
+    ctx download-resource [ "$DESTINATION" test_resource ]
+    ctx node attributes test_value = "$(cat "$DESTINATION")"
 }
 
 test_run_script_download_resource_and_render() {
-    local destination=$(mktemp)
-    ctx download-resource-and-render ${destination} test_resource
-    ctx node attributes test_value "$(cat ${destination})"
+    local DESTINATION=$(mktemp)
+    ctx download-resource-and-render [ "$DESTINATION" test_resource ]
+    ctx node attributes test_value = "$(cat "$DESTINATION")"
 }
 
 test_run_script_inputs_as_env_variables_no_override() {
-    ctx node attributes test_value "$custom_env_var"
+    ctx node attributes test_value = "$custom_env_var"
 }
 
 test_run_script_inputs_as_env_variables_process_env_override() {
-    ctx node attributes test_value "$custom_env_var"
+    ctx node attributes test_value = "$custom_env_var"
 }
 
 test_run_script_error_in_script() {
@@ -80,17 +80,17 @@ test_run_script_error_in_script() {
 }
 
 test_run_script_abort_immediate() {
-    ctx task abort abort-message
+    ctx task abort [ abort-message ]
 }
 
 test_run_script_retry() {
-    ctx task retry retry-message
+    ctx task retry [ retry-message ]
 }
 
 test_run_script_abort_error_ignored_by_script() {
     set +e
-    ctx task abort abort-message
+    ctx task abort [ abort-message ]
 }
 
 # Injected by test
-${test_operation} $@
+"$test_operation" "$@"

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/a43bc6b1/tox.ini
----------------------------------------------------------------------
diff --git a/tox.ini b/tox.ini
index 129c557..ff71e05 100644
--- a/tox.ini
+++ b/tox.ini
@@ -97,14 +97,14 @@ commands=
 [testenv:pylint_code]
 commands=
   pylint aria extensions/aria_extension_tosca/ \
-  --rcfile=aria/.pylintrc \
-  --disable=fixme,missing-docstring
+    --rcfile=aria/.pylintrc \
+    --disable=fixme,missing-docstring
 
 [testenv:pylint_tests]
 commands=
   pylint tests \
-  --rcfile=tests/.pylintrc \
-  --disable=fixme,missing-docstring
+    --rcfile=tests/.pylintrc \
+    --disable=fixme,missing-docstring
 
 [testenv:docs]
 install_command=