GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — pip_8.1.2 ( 36f804 )
by
unknown
06:44
created

PythonActionWrapper   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 98
rs 10
wmc 15

3 Methods

Rating   Name   Duplication   Size   Complexity  
B _get_action_instance() 0 22 4
B __init__() 0 37 3
D run() 0 36 8
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import sys
17
import json
18
import argparse
19
from oslo_config import cfg
20
21
from st2common import log as logging
22
from st2actions import config
23
from st2actions.runners.pythonrunner import Action
24
from st2actions.runners.utils import get_logger_for_python_runner_action
25
from st2actions.runners.utils import get_action_class_instance
26
from st2common.util import loader as action_loader
27
from st2common.util.config_loader import ContentPackConfigLoader
28
from st2common.constants.action import ACTION_OUTPUT_RESULT_DELIMITER
29
from st2common.constants.keyvalue import SYSTEM_SCOPE
30
from st2common.constants.runners import PYTHON_RUNNER_INVALID_ACTION_STATUS_EXIT_CODE
31
from st2common.service_setup import db_setup
32
from st2common.services.datastore import DatastoreService
33
34
__all__ = [
35
    'PythonActionWrapper',
36
    'ActionService'
37
]
38
39
LOG = logging.getLogger(__name__)
40
41
INVALID_STATUS_ERROR_MESSAGE = """
42
If this is an existing action which returns a tuple with two items, it needs to be updated to
43
either:
44
45
1. Return a list instead of a tuple
46
2. Return a tuple where a first items is a status flag - (True, ('item1', 'item2'))
47
48
For more information, please see: https://docs.stackstorm.com/upgrade_notes.html#st2-v1-6
49
""".strip()
50
51
52
class ActionService(object):
53
    """
54
    Instance of this class is passed to the action instance and exposes "public"
55
    methods which can be called by the action.
56
    """
57
58
    def __init__(self, action_wrapper):
59
        logger = get_logger_for_python_runner_action(action_name=action_wrapper._class_name)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _class_name was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
60
61
        self._action_wrapper = action_wrapper
62
        self._datastore_service = DatastoreService(logger=logger,
63
                                                   pack_name=self._action_wrapper._pack,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _pack was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
64
                                                   class_name=self._action_wrapper._class_name,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _class_name was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
65
                                                   api_username='action_service')
66
67
    ##################################
68
    # Methods for datastore management
69
    ##################################
70
71
    def list_values(self, local=True, prefix=None):
72
        return self._datastore_service.list_values(local, prefix)
73
74
    def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):
75
        return self._datastore_service.get_value(name, local, scope=scope, decrypt=decrypt)
76
77
    def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False):
78
        return self._datastore_service.set_value(name, value, ttl, local, scope=scope,
79
                                                 encrypt=encrypt)
80
81
    def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):
82
        return self._datastore_service.delete_value(name, local)
83
84
85
class PythonActionWrapper(object):
86
    def __init__(self, pack, file_path, parameters=None, user=None, parent_args=None):
0 ignored issues
show
Comprehensibility Bug introduced by
parent_args is re-defining a name which is already available in the outer-scope (previously defined on line 202).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
Comprehensibility Bug introduced by
user is re-defining a name which is already available in the outer-scope (previously defined on line 201).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
Comprehensibility Bug introduced by
parameters is re-defining a name which is already available in the outer-scope (previously defined on line 199).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
87
        """
88
        :param pack: Name of the pack this action belongs to.
89
        :type pack: ``str``
90
91
        :param file_path: Path to the action module.
92
        :type file_path: ``str``
93
94
        :param parameters: action parameters.
95
        :type parameters: ``dict`` or ``None``
96
97
        :param user: Name of the user who triggered this action execution.
98
        :type user: ``str``
99
100
        :param parent_args: Command line arguments passed to the parent process.
101
        :type parse_args: ``list``
102
        """
103
104
        self._pack = pack
105
        self._file_path = file_path
106
        self._parameters = parameters or {}
107
        self._user = user
108
        self._parent_args = parent_args or []
109
        self._class_name = None
110
        self._logger = logging.getLogger('PythonActionWrapper')
111
112
        try:
113
            config.parse_args(args=self._parent_args)
114
        except Exception:
115
            pass
116
117
        db_setup()
118
119
        # Note: We can only set a default user value if one is not provided after parsing the
120
        # config
121
        if not self._user:
122
            self._user = cfg.CONF.system_user.user
123
124
    def run(self):
125
        action = self._get_action_instance()
126
        output = action.run(**self._parameters)
127
128
        if isinstance(output, tuple) and len(output) == 2:
129
            # run() method returned status and data - (status, data)
130
            action_status = output[0]
131
            action_result = output[1]
132
        else:
133
            # run() method returned only data, no status (pre StackStorm v1.6)
134
            action_status = None
135
            action_result = output
136
137
        action_output = {
138
            'result': action_result,
139
            'status': None
140
        }
141
142
        if action_status is not None and not isinstance(action_status, bool):
143
            sys.stderr.write('Status returned from the action run() method must either be '
144
                             'True or False, got: %s\n' % (action_status))
145
            sys.stderr.write(INVALID_STATUS_ERROR_MESSAGE)
146
            sys.exit(PYTHON_RUNNER_INVALID_ACTION_STATUS_EXIT_CODE)
147
148
        if action_status is not None and isinstance(action_status, bool):
149
            action_output['status'] = action_status
150
151
        try:
152
            print_output = json.dumps(action_output)
153
        except Exception:
154
            print_output = str(action_output)
155
156
        # Print output to stdout so the parent can capture it
157
        sys.stdout.write(ACTION_OUTPUT_RESULT_DELIMITER)
158
        sys.stdout.write(print_output + '\n')
159
        sys.stdout.write(ACTION_OUTPUT_RESULT_DELIMITER)
160
161
    def _get_action_instance(self):
162
        actions_cls = action_loader.register_plugin(Action, self._file_path)
163
        action_cls = actions_cls[0] if actions_cls and len(actions_cls) > 0 else None
164
165
        if not action_cls:
166
            raise Exception('File "%s" has no action or the file doesn\'t exist.' %
167
                            (self._file_path))
168
169
        config_loader = ContentPackConfigLoader(pack_name=self._pack, user=self._user)
170
        config = config_loader.get_config()
0 ignored issues
show
Comprehensibility Bug introduced by
config is re-defining a name which is already available in the outer-scope (previously defined on line 22).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
171
172
        if config:
173
            LOG.info('Found config for action "%s"' % (self._file_path))
174
        else:
175
            LOG.info('No config found for action "%s"' % (self._file_path))
176
            config = None
177
178
        action_service = ActionService(action_wrapper=self)
179
        action_instance = get_action_class_instance(action_cls=action_cls,
180
                                                    config=config,
181
                                                    action_service=action_service)
182
        return action_instance
183
184
185
if __name__ == '__main__':
186
    parser = argparse.ArgumentParser(description='Python action runner process wrapper')
187
    parser.add_argument('--pack', required=True,
188
                        help='Name of the pack this action belongs to')
189
    parser.add_argument('--file-path', required=True,
190
                        help='Path to the action module')
191
    parser.add_argument('--parameters', required=False,
192
                        help='Serialized action parameters')
193
    parser.add_argument('--user', required=False,
194
                        help='User who triggered the action execution')
195
    parser.add_argument('--parent-args', required=False,
196
                        help='Command line arguments passed to the parent process')
197
    args = parser.parse_args()
198
199
    parameters = args.parameters
200
    parameters = json.loads(parameters) if parameters else {}
201
    user = args.user
202
    parent_args = json.loads(args.parent_args) if args.parent_args else []
203
204
    assert isinstance(parent_args, list)
205
    obj = PythonActionWrapper(pack=args.pack,
206
                              file_path=args.file_path,
207
                              parameters=parameters,
208
                              user=user,
209
                              parent_args=parent_args)
210
211
    obj.run()
212