Exemplo n.º 1
0
    def test_result_list_dataframe_returns_pandas_dict_if_called_directly(
            self):
        resultset = ResultList(name='test results')
        resultset.map_to(Issue())
        result_issue = Issue()
        result_issue.description = 'This is a test item'
        resultset.append(result_issue)

        another_result_issue = Issue()
        another_result_issue.description = 'This is a test item'
        resultset.append(another_result_issue)

        self.assertIsInstance(resultset.dataframe, pd.DataFrame)
Exemplo n.º 2
0
    def test_table_renders_filter_results_multi_results_without_combine(
            self, mock_heading, mock_table):
        self.tearDown()
        Config = namedtuple('Config', 'rows columns style')

        with patch('pyccata.core.filter.Filter.results',
                   new_callable=PropertyMock) as mock_results:
            results_set_one = ResultList(name='test results')
            result_issue = Issue()
            result_issue.description = 'This is a test item'
            results_set_one.append(result_issue)

            results_set_two = ResultList(name='another set of tests')
            another_result_issue = Issue()
            another_result_issue.description = 'This is a test item'
            results_set_two.append(another_result_issue)

            multi_results = MultiResultList()
            multi_results.combine = False
            multi_results.append(results_set_one)
            multi_results.append(results_set_two)

            mock_results.return_value = multi_results
            Filter = namedtuple('Filter', 'query max_results namespace')
            rows = Filter(query='project=mssportal',
                          max_results=5,
                          namespace='pyccata.core')

            columns = ['Name', 'Description']
            config = Config(rows=rows,
                            columns=columns,
                            style='Light heading 1')
            table = Table(self._thread_manager, config)

            document = ReportManager()
            table.render(document)
            self.assertEquals(2, mock_table.call_count)
            self.assertEquals(2, mock_heading.call_count)
Exemplo n.º 3
0
    def _convert_results(results):
        """
        Converts a ``jira.client.ResultList`` of ``jira.resources.Issues`` into a
        ``pyccata.core.resources.ResultList`` of ``pyccata.core.resources.Issues``
        """
        result_set = ResultList()
        result_set.total = results.total if hasattr(results,
                                                    'total') else len(results)
        for issue in results:
            item = Issue()
            item.key = getattr(issue, 'key') if hasattr(issue, 'key') else None
            item.summary = getattr(issue.fields, 'summary') if hasattr(
                issue.fields, 'summary') else None
            item.issuetype = getattr(issue.fields, 'issuetype') if hasattr(
                issue.fields, 'issuetype') else None
            item.created = getattr(issue.fields, 'created') if hasattr(
                issue.fields, 'created') else None
            item.updated = getattr(issue.fields, 'updated') if hasattr(
                issue.fields, 'updated') else None
            item.priority = getattr(issue.fields, 'priority') if hasattr(
                issue.fields, 'priority') else None
            item.description = getattr(issue.fields, 'description') if hasattr(
                issue.fields, 'description') else None
            item.status = getattr(issue.fields, 'status') if hasattr(
                issue.fields, 'status') else None
            item.project = getattr(issue.fields, 'project') if hasattr(
                issue.fields, 'project') else None
            item.fixVersions = getattr(issue.fields, 'fixVersions') if hasattr(
                issue.fields, 'fixVersions') else None
            item.resolution = getattr(issue.fields, 'resolution') if hasattr(
                issue.fields, 'resolution') else None
            item.resolutiondate = getattr(
                issue.fields, 'resolutiondate') if hasattr(
                    issue.fields, 'resolutiondate') else None
            item.creator = getattr(issue.fields, 'creator') if hasattr(
                issue.fields, 'creator') else None
            item.assignee = getattr(issue.fields, 'assignee') if hasattr(
                issue.fields, 'assignee') else None
            item.attachments = Jira._get_attachments(
                getattr(issue.fields, 'attachment'
                        ) if hasattr(issue.fields, 'attachment') else None)
            item.release_text = getattr(
                issue.fields, 'customfield_10600') if hasattr(
                    issue.fields, 'customfield_10600') else None
            item.business_representative = getattr(
                issue.fields, 'customfield_10700') if hasattr(
                    issue.fields, 'customfield_10700') else None
            item.rollout_instructions = getattr(
                issue.fields, 'customfield_10800') if hasattr(
                    issue.fields, 'customfield_10800') else None
            item.rollback_instructions = getattr(
                issue.fields, 'customfield_10801') if hasattr(
                    issue.fields, 'customfield_10801') else None

            if hasattr(issue.fields, 'customfield_10802'):
                item.pipelines = [
                    item.value
                    for item in getattr(issue.fields, 'customfield_10802')
                ]

            result_set.append(item)
        return result_set
Exemplo n.º 4
0
class ThreadableCommand(Threadable):
    """
    This class can be used to process shell commands
    """
    # pylint: disable=too-many-instance-attributes,too-many-public-methods
    # It is understood that this class requires a number of attributes and
    # accessor methods.

    MAX_PRIORITY = 10000
    PRIORITY = 0
    _redirect_regex = None
    _commands = None
    _redirects = None

    PRIORITY = 1000
    _results = None
    _block = None

    @property
    def results(self):
        """
        Get the results of command
        """
        return self._results

    @property
    def threadmanager(self):
        """ Get the current loaded threadmanager """
        return self._thread_manager

    @accepts(ThreadManager, tuple, append=bool)
    def __init__(self, threadmanager, config, append=True):
        """
        Initialise the ThreadableCommand object
        """
        self._thread_manager = threadmanager
        self._commands = []
        self._observers = []
        self._redirects = []
        self._results = ResultList()

        # pylint: disable=protected-access
        # verification that Configuration is not initialised and initialise it
        # with the thread manager - this is desired behaviour against the singleton
        # to prevent it being loaded multiple times inside threading objects

        if Configuration._instance is None:
            Configuration._instance = self.threadmanager.configuration
        self.validate_setup(config)

        super().__init__(**config._asdict())
        if append:
            self.threadmanager.append(self)

    @accepts(
        name=str,
        command=str,
        input_directory=(None, str),
        output_directory=(None, str),
        wait_for=(None, str, Threadable)
    )
    def setup(self, name='', command='', input_directory=None, output_directory=None, wait_for=None):
        """
        Sets up the thread and builds the command structure.

        @param name            string
        @param command          string
        @param input_directory  string or None
        @param output_directory string or None
        @param wait_for         string, None or ThreadableCommand

        If this method is overridden, the overriding method must call back up to parent
        or implement this functionality directly. Failure to do this will prevent the thread
        from executing.

        The flag `wait_for` will prevent the thread from executing until the wait_for thread
        has completed. This is useful in the event a command needs the complete output from
        another thread before it can work, for example, downloading data then parsing it.

        If `wait_for` is None, the thread will execute as soon as there is room in the pool.
        (Default pool size = ThreadManager.POOL_SIZE).
        """
        # pylint: disable=arguments-differ
        # This method provides specific implementations of *args and **kwargs

        # pylint: disable=too-many-arguments
        # This method requires a larger number of arguments than the standard
        self.thread_name = name
        self._input_directory = Replacements().replace(input_directory)
        self._output_directory = Replacements().replace(
            output_directory,
            additional=ThreadableCommand.replacements(output_directory)
        )

        self._build_command(command)
        self._block = wait_for

    @accepts(str)
    def _build_command(self, command_string):
        """
        Breaks down a command string and returns a list of commands initialised for subprocess.Popen

        @param command_string string
        """
        command_list = command_string.split(' | ')
        for command_structure in command_list:
            command = Command()
            redirect_regex = re.compile(
                r"(?P<command>.*?)( ((?P<redirect>[&\d]?)>+ ?&?(?P<filename>\S+)))( ?< ?(?P<infile>.*))?",
                re.DOTALL
            )

            matches = [match.groupdict() for match in redirect_regex.finditer(command_structure)]
            if len(matches) == 0:
                try:
                    structure = shlex.split(command_structure)
                except ValueError as exception:
                    Logger().error('Failed to parse command \'' + command_structure + '\'')
                    raise

                command.command = structure[0]
                command.arguments = structure[1:]
            else:
                try:
                    structure = shlex.split(matches[0]['command'])
                except ValueError as exception:
                    Logger().error('Failed to parse command \'' + matches[0]['command'] + '\'')
                    raise exception
                command.command = structure[0]
                command.arguments = structure[1:]
                for match in matches:
                    command.redirects.append(
                        Redirect(
                            redirect_input=(
                                int(match['redirect']) if match['redirect'].isdigit() else match['redirect']
                            ),
                            redirect_output=int(match['filename']) if match['filename'].isdigit() else match['filename']
                        )
                    )
            self._commands.append(command)

    def run(self):
        """
        Executes the current thread
        """
        processes = []
        for command in self._commands:
            last_pipe = processes[-1].stdout if len(processes) > 0 else None
            processes.append(
                Popen(
                    [command.command] + command.arguments,
                    stdin=last_pipe,
                    stdout=command.stdout,
                    stderr=command.stderr
                )
            )
            command.return_code = processes[-1].poll()

        if processes[-1].stdout is not None and hasattr(processes[-1].stdout, 'readline'):
            for line in iter(processes[-1].stdout.readline, b''):
                item = CommandLineResultItem()
                item.line = line.decode('utf8').strip()
                self._results.append(item)

        stderr = []
        if processes[-1].stderr is not None:
            for line in iter(processes[-1].stderr.readline, b''):
                stderr.append(line.decode('utf8').strip())
        processes[-1].communicate()
        if len(stderr) != 0:
            self.failure = ThreadFailedError(stderr)
        self._complete = True

    @staticmethod
    def replacements(string_to_search):
        """
        Compiles a list of optional string replacements for command thread strings
        """
        replacements = {}
        function_regex = re.compile(
            r'.*?[-_.]{1}\{(?P<what>.*?)\.(?P<command>.*?)\}',
            re.DOTALL
        )
        matches = [match.groupdict() for match in function_regex.finditer(string_to_search)]
        if len(matches) == 0:
            return None

        for match in matches:
            string = '{0}.{1}'.format(match['what'], match['command'])
            what = match['what']
            if Replacements().find(what.upper()):
                what = Replacements().replace('{' + what.upper() + '}')
            command = '{0}_helper'.format(match['command'])
            try:
                command = include(command, 'pyccata', 'helpers')
            except InvalidModuleError:
                Logger().error(
                    'Invalid helper method {0} specified for {1}'.format(
                        match['command'],
                        string
                    )
                )
                raise
            replacements[string] = command(what)
        return replacements

    @staticmethod
    def logdir():
        """ Get a log directory for the command output """
        path = os.path.join(Replacements().replace('{BASE_PATH}'), 'log')
        if not os.path.exists(path):
            os.makedirs(path)
        return path