<commit_before># -*- coding: utf-8 -*-

"""
    This radish extension provides the functionality to write the feature file run to the console.
"""

# disable no-member lint error because of dynamic method from colorful
# pylint: disable=no-member

import os
from colorful import colorful

from radish.terrain import world
from radish.hookregistry import before, after
from radish.feature import Feature
from radish.scenariooutline import ScenarioOutline
from radish.scenarioloop import ScenarioLoop
from radish.stepmodel import Step
from radish.extensionregistry import extension
from radish.utils import console_write as write


@extension
class ConsoleWriter(object):
    """
        Console writer radish extension
    """
    OPTIONS = [
        ("--no-ansi", "print features without any ANSI sequences (like colors, line jump)"),
        ("--no-line-jump", "print features without line jumps (overwriting steps)"),
        ("--write-steps-once", "does not rewrite the steps (this option only makes sense in combination with the --no-ansi flag)"),
        ("--write-ids", "write the feature, scenario and step id before the sentences")
    ]
    LOAD_IF = staticmethod(lambda config: True)
    LOAD_PRIORITY = 30

    def __init__(self):
        before.each_feature(self.console_writer_before_each_feature)
        before.each_scenario(self.console_writer_before_each_scenario)
        before.each_step(self.console_writer_before_each_step)
        after.each_feature(self.console_writer_after_each_feature)
        after.each_scenario(self.console_writer_after_each_scenario)
        after.each_step(self.console_writer_after_each_step)

        self.last_precondition = None

    def get_color_func(self, state):
        """
            Returns the color func to use
        """
        if state == Step.State.PASSED:
            return colorful.bold_green
        elif state == Step.State.FAILED:
            return colorful.bold_red
        elif state == Step.State.PENDING:
            return colorful.bold_brown
        elif state:
            return colorful.cyan

    def get_line_jump_seq(self):
        """
            Returns the line jump ANSI sequence
        """
        line_jump_seq = ""
        if not world.config.no_ansi and not world.config.no_line_jump and not world.config.write_steps_once:
            line_jump_seq = "\r\033[A\033[K"
        return line_jump_seq

    def get_id_sentence_prefix(self, model, color_func, max_rows=None):
        """
            Returns the id from a model as sentence prefix

            :param Model model: a model with an id property
            :param function color_func: a function which gives coloring
            :param int max_rows: the maximum rows. Used for padding
        """
        padding = len("{0}. ".format(max_rows)) if max_rows else 0
        return color_func("{1: >{0}}. ".format(padding, model.id)) if world.config.write_ids else ""

    def get_id_padding(self, max_rows):
        """
            Returns the id padding
        """
        if not world.config.write_ids:
            return ""

        return " " * (len(str(max_rows)) + 2)

    def get_table_col_widths(self, table):
        """
            Returns the width for every column of a table (lists in list)
        """
        return [max(len(str(col)) for col in row) for row in zip(*table)]  # pylint: disable=star-args

    def console_writer_before_each_feature(self, feature):
        """
            Writes feature header to the console

            :param Feature feature: the feature to write to the console
        """
        output = ""
        for tag in feature.tags:
            output += colorful.cyan(u"@{0}{1}\n".format(tag.name, "({0})".format(tag.arg) if tag.arg else ""))

        leading = "\n    " if feature.description else ""

        output += u"{0}{1}: {2}  # {3}{4}{5}".format(
            self.get_id_sentence_prefix(feature, colorful.bold_cyan),
            colorful.bold_white(feature.keyword),
            colorful.bold_white(feature.sentence),
            colorful.bold_black(feature.path),
            leading,
            colorful.white("\n    ".join(feature.description))
        )
        write(output)

    def console_writer_before_each_scenario(self, scenario):
        """
            Writes the scenario header to the console

            :param Scenario scenario: the scenario to write to the console
        """
        output = "\n"
        if isinstance(scenario.parent, ScenarioOutline):
            if world.config.write_steps_once:
                return

            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_brown, len(scenario.parent.scenarios))
            colored_pipe = colorful.bold_white("|")
            output = u"        {0}{1} {2} {1}".format(
                id_prefix,
                colored_pipe,
                (u" {0} ").format(colored_pipe).join(
                    colorful.bold_brown(u"{1: <{0}}".format(scenario.parent.get_column_width(i), x)) for i, x in enumerate(scenario.example.data)
                )
            )
        elif isinstance(scenario.parent, ScenarioLoop):
            if world.config.write_steps_once:
                return

            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_brown, len(scenario.parent.scenarios))
            colored_pipe = colorful.bold_white("|")
            output = u"        {0}{1} {2: <18} {1}".format(id_prefix, colored_pipe, colorful.bold_brown(scenario.iteration))
        else:
            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_cyan)
            for tag in scenario.tags:
                if tag.name == "precondition" and world.config.expand and world.config.show:  # exceptional for show command when scenario steps expand and tag is a precondition -> comment it out
                    output += colorful.white(u"    # @{0}{1}\n".format(tag.name, "({0})".format(tag.arg) if tag.arg else ""))
                else:
                    output += colorful.cyan(u"    @{0}{1}\n".format(tag.name, u"({0})".format(tag.arg) if tag.arg else ""))
            output += u"    {0}{1}: {2}".format(id_prefix, colorful.bold_white(scenario.keyword), colorful.bold_white(scenario.sentence))
        write(output)

    def console_writer_before_each_step(self, step):
        """
            Writes the step to the console before it is run

            :param Step step: the step to write to the console
        """
        if not isinstance(step.parent.parent, Feature):
            return

        if world.config.write_steps_once:
            return

        output = ""
        if step.as_precondition and self.last_precondition != step.as_precondition:
            output += colorful.white(u"      As precondition from {0}: {1}\n".format(os.path.basename(step.as_precondition.path), step.as_precondition.sentence))
        elif not step.as_precondition and self.last_precondition:
            output += colorful.white(u"      From scenario\n")

        self.last_precondition = step.as_precondition
        output += u"\r        {0}{1}".format(self.get_id_sentence_prefix(step, colorful.bold_brown), colorful.bold_brown(step.sentence))

        if step.text:
            id_padding = self.get_id_padding(len(step.parent.steps))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))
            output += colorful.cyan(u"".join(["\n                {0}{1}".format(id_padding, l) for l in step.raw_text]))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))

        if step.table:
            colored_pipe = colorful.bold_white("|")
            col_widths = self.get_table_col_widths(step.table)
            for row in step.table:
                output += u"\n          {0} {1} {0}".format(colored_pipe, (" {0} ").format(colored_pipe).join(
                    colorful.bold_brown(u"{1: <{0}}".format(col_widths[i], x)) for i, x in enumerate(row)
                ))

        write(output)

    def console_writer_after_each_step(self, step):
        """
            Writes the step to the console after it was run

            :param Step step: the step to write to the console
        """
        if not isinstance(step.parent.parent, Feature):
            return

        color_func = self.get_color_func(step.state)
        line_jump_seq = self.get_line_jump_seq() * (((len(step.raw_text) + 3) if step.text else 1) + (len(step.table) if step.table else 0))
        output = u"{0}        {1}{2}".format(line_jump_seq, self.get_id_sentence_prefix(step, colorful.bold_cyan), color_func(step.sentence))

        if step.text:
            id_padding = self.get_id_padding(len(step.parent.steps))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))
            output += colorful.cyan(u"".join(["\n                {0}{1}".format(id_padding, l) for l in step.raw_text]))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))

        if step.table:
            colored_pipe = colorful.bold_white("|")
            col_widths = self.get_table_col_widths(step.table)
            for row in step.table:
                output += u"\n          {0} {1} {0}".format(colored_pipe, (" {0} ").format(colored_pipe).join(
                    color_func(u"{1: <{0}}".format(col_widths[i], x)) for i, x in enumerate(row)
                ))

        if step.state == step.State.FAILED:
            if world.config.with_traceback:
                output += u"\n          {0}{1}".format(self.get_id_padding(len(step.parent.steps) - 2), "\n          ".join([colorful.red(l) for l in step.failure.traceback.split("\n")[:-2]]))
            output += u"\n          {0}{1}: {2}".format(self.get_id_padding(len(step.parent.steps) - 2), colorful.bold_red(step.failure.name), colorful.red(step.failure.reason))

        write(output)

    def console_writer_after_each_scenario(self, scenario):
        """
            If the scenario is a ExampleScenario it will write the Examples header

            :param Scenario scenario: the scenario which was ran.
        """
        output = ""
        if isinstance(scenario, ScenarioOutline):
            output += u"\n    {0}:\n".format(colorful.bold_white(scenario.example_keyword))
            output += colorful.bold_white(u"        {0}| {1} |".format(
                self.get_id_padding(len(scenario.scenarios)),
                u" | ".join("{1: <{0}}".format(scenario.get_column_width(i), x) for i, x in enumerate(scenario.examples_header))
            ))
        elif isinstance(scenario, ScenarioLoop):
            output += u"\n    {0}: {1}".format(colorful.bold_white(scenario.iterations_keyword), colorful.cyan(scenario.iterations))
        elif isinstance(scenario.parent, ScenarioOutline):
            colored_pipe = colorful.bold_white("|")
            color_func = self.get_color_func(scenario.state)
            output += u"{0}        {1}{2} {3} {2}".format(
                self.get_line_jump_seq(),
                self.get_id_sentence_prefix(scenario, colorful.bold_cyan, len(scenario.parent.scenarios)),
                colored_pipe,
                (u" {0} ").format(colored_pipe).join(
                    color_func(u"{1: <{0}}".format(scenario.parent.get_column_width(i), x)) for i, x in enumerate(scenario.example.data)<commit_msg>Fix Scenario Outline Example header indentation when writing ids. Closes #36<commit_after># -*- coding: utf-8 -*-

"""
    This radish extension provides the functionality to write the feature file run to the console.
"""

# disable no-member lint error because of dynamic method from colorful
# pylint: disable=no-member

import os
from colorful import colorful

from radish.terrain import world
from radish.hookregistry import before, after
from radish.feature import Feature
from radish.scenariooutline import ScenarioOutline
from radish.scenarioloop import ScenarioLoop
from radish.stepmodel import Step
from radish.extensionregistry import extension
from radish.utils import console_write as write


@extension
class ConsoleWriter(object):
    """
        Console writer radish extension
    """
    OPTIONS = [
        ("--no-ansi", "print features without any ANSI sequences (like colors, line jump)"),
        ("--no-line-jump", "print features without line jumps (overwriting steps)"),
        ("--write-steps-once", "does not rewrite the steps (this option only makes sense in combination with the --no-ansi flag)"),
        ("--write-ids", "write the feature, scenario and step id before the sentences")
    ]
    LOAD_IF = staticmethod(lambda config: True)
    LOAD_PRIORITY = 30

    def __init__(self):
        before.each_feature(self.console_writer_before_each_feature)
        before.each_scenario(self.console_writer_before_each_scenario)
        before.each_step(self.console_writer_before_each_step)
        after.each_feature(self.console_writer_after_each_feature)
        after.each_scenario(self.console_writer_after_each_scenario)
        after.each_step(self.console_writer_after_each_step)

        self.last_precondition = None

    def get_color_func(self, state):
        """
            Returns the color func to use
        """
        if state == Step.State.PASSED:
            return colorful.bold_green
        elif state == Step.State.FAILED:
            return colorful.bold_red
        elif state == Step.State.PENDING:
            return colorful.bold_brown
        elif state:
            return colorful.cyan

    def get_line_jump_seq(self):
        """
            Returns the line jump ANSI sequence
        """
        line_jump_seq = ""
        if not world.config.no_ansi and not world.config.no_line_jump and not world.config.write_steps_once:
            line_jump_seq = "\r\033[A\033[K"
        return line_jump_seq

    def get_id_sentence_prefix(self, model, color_func, max_rows=None):
        """
            Returns the id from a model as sentence prefix

            :param Model model: a model with an id property
            :param function color_func: a function which gives coloring
            :param int max_rows: the maximum rows. Used for padding
        """
        padding = len("{0}. ".format(max_rows)) if max_rows else 0
        return color_func("{1: >{0}}. ".format(padding, model.id)) if world.config.write_ids else ""

    def get_id_padding(self, max_rows, offset=0):
        """
            Returns the id padding
        """
        if not world.config.write_ids:
            return ""

        return " " * (len(str(max_rows)) + 2 + offset)

    def get_table_col_widths(self, table):
        """
            Returns the width for every column of a table (lists in list)
        """
        return [max(len(str(col)) for col in row) for row in zip(*table)]  # pylint: disable=star-args

    def console_writer_before_each_feature(self, feature):
        """
            Writes feature header to the console

            :param Feature feature: the feature to write to the console
        """
        output = ""
        for tag in feature.tags:
            output += colorful.cyan(u"@{0}{1}\n".format(tag.name, "({0})".format(tag.arg) if tag.arg else ""))

        leading = "\n    " if feature.description else ""

        output += u"{0}{1}: {2}  # {3}{4}{5}".format(
            self.get_id_sentence_prefix(feature, colorful.bold_cyan),
            colorful.bold_white(feature.keyword),
            colorful.bold_white(feature.sentence),
            colorful.bold_black(feature.path),
            leading,
            colorful.white("\n    ".join(feature.description))
        )
        write(output)

    def console_writer_before_each_scenario(self, scenario):
        """
            Writes the scenario header to the console

            :param Scenario scenario: the scenario to write to the console
        """
        output = "\n"
        if isinstance(scenario.parent, ScenarioOutline):
            if world.config.write_steps_once:
                return

            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_brown, len(scenario.parent.scenarios))
            colored_pipe = colorful.bold_white("|")
            output = u"        {0}{1} {2} {1}".format(
                id_prefix,
                colored_pipe,
                (u" {0} ").format(colored_pipe).join(
                    colorful.bold_brown(u"{1: <{0}}".format(scenario.parent.get_column_width(i), x)) for i, x in enumerate(scenario.example.data)
                )
            )
        elif isinstance(scenario.parent, ScenarioLoop):
            if world.config.write_steps_once:
                return

            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_brown, len(scenario.parent.scenarios))
            colored_pipe = colorful.bold_white("|")
            output = u"        {0}{1} {2: <18} {1}".format(id_prefix, colored_pipe, colorful.bold_brown(scenario.iteration))
        else:
            id_prefix = self.get_id_sentence_prefix(scenario, colorful.bold_cyan)
            for tag in scenario.tags:
                if tag.name == "precondition" and world.config.expand and world.config.show:  # exceptional for show command when scenario steps expand and tag is a precondition -> comment it out
                    output += colorful.white(u"    # @{0}{1}\n".format(tag.name, "({0})".format(tag.arg) if tag.arg else ""))
                else:
                    output += colorful.cyan(u"    @{0}{1}\n".format(tag.name, u"({0})".format(tag.arg) if tag.arg else ""))
            output += u"    {0}{1}: {2}".format(id_prefix, colorful.bold_white(scenario.keyword), colorful.bold_white(scenario.sentence))
        write(output)

    def console_writer_before_each_step(self, step):
        """
            Writes the step to the console before it is run

            :param Step step: the step to write to the console
        """
        if not isinstance(step.parent.parent, Feature):
            return

        if world.config.write_steps_once:
            return

        output = ""
        if step.as_precondition and self.last_precondition != step.as_precondition:
            output += colorful.white(u"      As precondition from {0}: {1}\n".format(os.path.basename(step.as_precondition.path), step.as_precondition.sentence))
        elif not step.as_precondition and self.last_precondition:
            output += colorful.white(u"      From scenario\n")

        self.last_precondition = step.as_precondition
        output += u"\r        {0}{1}".format(self.get_id_sentence_prefix(step, colorful.bold_brown), colorful.bold_brown(step.sentence))

        if step.text:
            id_padding = self.get_id_padding(len(step.parent.steps))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))
            output += colorful.cyan(u"".join(["\n                {0}{1}".format(id_padding, l) for l in step.raw_text]))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))

        if step.table:
            colored_pipe = colorful.bold_white("|")
            col_widths = self.get_table_col_widths(step.table)
            for row in step.table:
                output += u"\n          {0} {1} {0}".format(colored_pipe, (" {0} ").format(colored_pipe).join(
                    colorful.bold_brown(u"{1: <{0}}".format(col_widths[i], x)) for i, x in enumerate(row)
                ))

        write(output)

    def console_writer_after_each_step(self, step):
        """
            Writes the step to the console after it was run

            :param Step step: the step to write to the console
        """
        if not isinstance(step.parent.parent, Feature):
            return

        color_func = self.get_color_func(step.state)
        line_jump_seq = self.get_line_jump_seq() * (((len(step.raw_text) + 3) if step.text else 1) + (len(step.table) if step.table else 0))
        output = u"{0}        {1}{2}".format(line_jump_seq, self.get_id_sentence_prefix(step, colorful.bold_cyan), color_func(step.sentence))

        if step.text:
            id_padding = self.get_id_padding(len(step.parent.steps))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))
            output += colorful.cyan(u"".join(["\n                {0}{1}".format(id_padding, l) for l in step.raw_text]))
            output += colorful.bold_white(u'\n            {0}"""'.format(id_padding))

        if step.table:
            colored_pipe = colorful.bold_white("|")
            col_widths = self.get_table_col_widths(step.table)
            for row in step.table:
                output += u"\n          {0} {1} {0}".format(colored_pipe, (" {0} ").format(colored_pipe).join(
                    color_func(u"{1: <{0}}".format(col_widths[i], x)) for i, x in enumerate(row)
                ))

        if step.state == step.State.FAILED:
            if world.config.with_traceback:
                output += u"\n          {0}{1}".format(self.get_id_padding(len(step.parent.steps) - 2), "\n          ".join([colorful.red(l) for l in step.failure.traceback.split("\n")[:-2]]))
            output += u"\n          {0}{1}: {2}".format(self.get_id_padding(len(step.parent.steps) - 2), colorful.bold_red(step.failure.name), colorful.red(step.failure.reason))

        write(output)

    def console_writer_after_each_scenario(self, scenario):
        """
            If the scenario is a ExampleScenario it will write the Examples header

            :param Scenario scenario: the scenario which was ran.
        """
        output = ""
        if isinstance(scenario, ScenarioOutline):
            output += u"\n    {0}:\n".format(colorful.bold_white(scenario.example_keyword))
            output += colorful.bold_white(u"        {0}| {1} |".format(
                self.get_id_padding(len(scenario.scenarios), offset=2),
                u" | ".join("{1: <{0}}".format(scenario.get_column_width(i), x) for i, x in enumerate(scenario.examples_header))
            ))
        elif isinstance(scenario, ScenarioLoop):
            output += u"\n    {0}: {1}".format(colorful.bold_white(scenario.iterations_keyword), colorful.cyan(scenario.iterations))
        elif isinstance(scenario.parent, ScenarioOutline):
            colored_pipe = colorful.bold_white("|")
            color_func = self.get_color_func(scenario.state)
            output += u"{0}        {1}{2} {3} {2}".format(
                self.get_line_jump_seq(),
                self.get_id_sentence_prefix(scenario, colorful.bold_cyan, len(scenario.parent.scenarios)),
                colored_pipe,
                (u" {0} ").format(colored_pipe).join(
                    color_func(u"{1: <{0}}".format(scenario.parent.get_column_width(i), x)) for i, x in enumerate(scenario.example.data)
{'fraction_non_alphanumeric': 0.1199385155906895, 'fraction_numerical': 0.00878348704435661, 'mean_word_length': 2.341796301731729, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4314587', 'n_tokens_mistral': 6954, 'n_tokens_neox': 6627, 'n_words': 1536}