Skip to content

Commit

Permalink
Result.py: Add actions attribute
Browse files Browse the repository at this point in the history
This adds support for bears to define their own custom actions. A new
`actions` attribute is added to Result class. It is a list of action
objects defined by the bears.
This also adds a new attribute `identity` to `get_metadata` function is
ResultAction module. This is used as a key for `action_dict`.
Also made changes in ConsoleInteraction and Processing module to add
actions to list of default actions.
  • Loading branch information
akshatkarani committed Sep 1, 2019
1 parent ac3c3b1 commit bcee2cd
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 89 deletions.
46 changes: 24 additions & 22 deletions coalib/output/ConsoleInteraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ def acquire_actions_and_apply(console_printer,
action_dict = {}
metadata_list = []

for action in cli_actions:
for action in list(cli_actions) + result.actions:
if action.is_applicable(result,
file_dict,
file_diff_dict,
tuple(applied_actions.keys())) is True:
metadata = action.get_metadata()
action_dict[metadata.name] = action
action_dict[metadata.id] = action
metadata_list.append(metadata)

if not metadata_list:
Expand Down Expand Up @@ -642,12 +642,13 @@ def choose_action(console_printer, actions, apply_single=False):
with the description of the actions.
"""
actions_desc = []
actions_name = []
actions_id = []
do_nothing_action = actions[0]
if apply_single:
for i, action in enumerate(actions, 0):
if apply_single == action.desc:
return ([action.desc], [action.name])
return (['Do (N)othing'], ['Do (N)othing'])
return ([action.desc], [action.id])
return ([do_nothing_action.desc], [do_nothing_action.id])
else:
while True:
for i, action in enumerate(actions, 0):
Expand All @@ -669,24 +670,24 @@ def choose_action(console_printer, actions, apply_single=False):
c = int(c)
if i == c:
actions_desc.append(action.desc)
actions_name.append(action.name)
actions_id.append(action.id)
break
elif c.isalpha():
c = c.upper()
c = '(' + c + ')'
for i, action in enumerate(actions, 1):
if c in action.desc:
actions_desc.append(action.desc)
actions_name.append(action.name)
actions_id.append(action.id)
break
if actions_desc_len == len(actions_desc):
console_printer.print(STR_INVALID_OPTION.format(str_c),
color=WARNING_COLOR)

if not choice:
actions_desc.append(DoNothingAction().get_metadata().desc)
actions_name.append(DoNothingAction().get_metadata().name)
return (actions_desc, actions_name)
actions_desc.append(do_nothing_action.desc)
actions_id.append(do_nothing_action.id)
return (actions_desc, actions_id)


def try_to_apply_action(action_name,
Expand Down Expand Up @@ -731,10 +732,10 @@ def try_to_apply_action(action_name,
console_printer.print(
format_lines(chosen_action.SUCCESS_MESSAGE, symbol='['),
color=SUCCESS_COLOR)
applied_actions[action_name] = [copy.copy(result), copy.copy(
file_dict),
copy.copy(file_diff_dict),
copy.copy(section)]
applied_actions[action_name] = [copy.copy(result),
copy.copy(file_dict),
copy.copy(file_diff_dict),
copy.copy(section)]
result.set_applied_actions(applied_actions)
failed_actions.discard(action_name)
except Exception as exception: # pylint: disable=broad-except
Expand Down Expand Up @@ -781,17 +782,17 @@ def ask_for_action_and_apply(console_printer,
"""
do_nothing_action = DoNothingAction()
metadata_list.insert(0, do_nothing_action.get_metadata())
action_dict[do_nothing_action.get_metadata().name] = DoNothingAction()
action_dict[do_nothing_action.get_metadata().id] = DoNothingAction()

actions_desc, actions_name = choose_action(console_printer, metadata_list,
apply_single)
actions_desc, actions_id = choose_action(console_printer, metadata_list,
apply_single)

if apply_single:
for index, action_details in enumerate(metadata_list, 1):
if apply_single == action_details.desc:
action_name, section = get_action_info(
section, metadata_list[index - 1], failed_actions)
chosen_action = action_dict[action_details.name]
chosen_action = action_dict[action_details.id]
try_to_apply_action(action_name,
chosen_action,
console_printer,
Expand All @@ -803,14 +804,15 @@ def ask_for_action_and_apply(console_printer,
file_diff_dict,
file_dict,
applied_actions)
break
return False
else:
for action_choice, action_choice_name in zip(actions_desc,
actions_name):
chosen_action = action_dict[action_choice_name]
for action_choice, action_choice_id in zip(actions_desc,
actions_id):
chosen_action = action_dict[action_choice_id]
action_choice_made = action_choice
for index, action_details in enumerate(metadata_list, 1):
if action_choice_made in action_details.desc:
if action_choice_made == action_details.desc:
action_name, section = get_action_info(
section, metadata_list[index-1], failed_actions)
try_to_apply_action(action_name,
Expand Down
81 changes: 44 additions & 37 deletions coalib/processes/Processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
from coalib.io.File import File


ACTIONS = [DoNothingAction,
ApplyPatchAction,
PrintDebugMessageAction,
ShowPatchAction,
IgnoreResultAction,
ShowAppliedPatchesAction,
GeneratePatchesAction]
ACTIONS = [DoNothingAction(),
ApplyPatchAction(),
PrintDebugMessageAction(),
ShowPatchAction(),
IgnoreResultAction(),
ShowAppliedPatchesAction(),
GeneratePatchesAction()]


def get_cpu_count():
Expand Down Expand Up @@ -74,21 +74,23 @@ def create_process_group(command_array, **kwargs):
return proc


def get_default_actions(section):
def get_default_actions(section, bear_actions):
"""
Parses the key ``default_actions`` in the given section.
:param section: The section where to parse from.
:return: A dict with the bearname as keys and their default
actions as values and another dict that contains bears
and invalid action names.
:param section: The section where to parse from.
:param bear_actions: List of all the bear defined actions.
:return: A dict with the bearname as keys and their default
actions as values and another dict that contains bears
and invalid action names.
"""
try:
default_actions = dict(section['default_actions'])
except IndexError:
return {}, {}

action_dict = {action.get_metadata().name: action for action in ACTIONS}
action_dict = {action.get_metadata().name: action
for action in ACTIONS + bear_actions}
invalid_action_set = default_actions.values() - action_dict.keys()
invalid_actions = {}
if len(invalid_action_set) != 0:
Expand Down Expand Up @@ -121,8 +123,11 @@ def autoapply_actions(results,
:param log_printer: A log printer instance to log messages on.
:return: A list of unprocessed results.
"""

default_actions, invalid_actions = get_default_actions(section)
bear_actions = []
for result in results:
bear_actions += result.actions
default_actions, invalid_actions = get_default_actions(section,
bear_actions)
no_autoapply_warn = bool(section.get('no_autoapply_warn', False))
for bearname, actionname in invalid_actions.items():
logging.warning('Selected default action {!r} for bear {!r} does not '
Expand All @@ -145,30 +150,32 @@ def autoapply_actions(results,
else:
not_processed_results.append(result)
continue
if action not in bear_actions or action in result.actions:
applicable = action.is_applicable(result, file_dict, file_diff_dict)
if applicable is not True:
if not no_autoapply_warn:
logging.warning('{}: {}'.format(result.origin, applicable))
not_processed_results.append(result)
continue

applicable = action.is_applicable(result, file_dict, file_diff_dict)
if applicable is not True:
if not no_autoapply_warn:
logging.warning('{}: {}'.format(result.origin, applicable))
not_processed_results.append(result)
continue

try:
action().apply_from_section(result,
file_dict,
file_diff_dict,
section)
logging.info('Applied {!r} on {} from {!r}.'.format(
action.get_metadata().name,
result.location_repr(),
result.origin))
except Exception as ex:
try:
action.apply_from_section(result,
file_dict,
file_diff_dict,
section)
logging.info('Applied {!r} on {} from {!r}.'.format(
action.get_metadata().name,
result.location_repr(),
result.origin))
except Exception as ex:
not_processed_results.append(result)
log_exception(
'Failed to execute action {!r} with error: {}.'.format(
action.get_metadata().name, ex),
ex)
logging.debug('-> for result ' + repr(result) + '.')
else:
not_processed_results.append(result)
log_exception(
'Failed to execute action {!r} with error: {}.'.format(
action.get_metadata().name, ex),
ex)
logging.debug('-> for result ' + repr(result) + '.')

return not_processed_results

Expand Down
14 changes: 11 additions & 3 deletions coalib/results/Result.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def __init__(self,
confidence: int = 100,
aspect: (aspectbase, None) = None,
message_arguments: dict = {},
applied_actions: dict = {}):
applied_actions: dict = {},
actions: list = []):
"""
:param origin:
Class name or creator object of this object.
Expand Down Expand Up @@ -99,6 +100,8 @@ def __init__(self,
:param applied_actions:
A dictionary that contains the result, file_dict, file_diff_dict and
the section for an action.
:param actions:
A list of action instances specific to the origin of the result.
:raises ValueError:
Raised when confidence is not between 0 and 100.
:raises KeyError:
Expand Down Expand Up @@ -131,6 +134,7 @@ def __init__(self,
if self.aspect and not self.additional_info:
self.additional_info = '{} {}'.format(
aspect.Docs.importance_reason, aspect.Docs.fix_suggestions)
self.actions = actions

@property
def message(self):
Expand Down Expand Up @@ -164,7 +168,8 @@ def from_values(cls,
diffs: (dict, None) = None,
confidence: int = 100,
aspect: (aspectbase, None) = None,
message_arguments: dict = {}):
message_arguments: dict = {},
actions: list = []):
"""
Creates a result with only one SourceRange with the given start and end
locations.
Expand Down Expand Up @@ -209,6 +214,8 @@ def from_values(cls,
should be a leaf of the aspect tree! (If you have a node, spend
some time figuring out which of the leafs exactly your result
belongs to.)
:param actions:
A list of action instances specific to the origin of the result.
"""
source_range = SourceRange.from_values(file,
line,
Expand All @@ -225,7 +232,8 @@ def from_values(cls,
diffs=diffs,
confidence=confidence,
aspect=aspect,
message_arguments=message_arguments)
message_arguments=message_arguments,
actions=actions)

def to_string_dict(self):
"""
Expand Down
10 changes: 6 additions & 4 deletions coalib/results/result_actions/ResultAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ def apply_from_section(self,
params = self.get_metadata().create_params_from_section(section)
return self.apply(result, original_file_dict, file_diff_dict, **params)

@classmethod
def get_metadata(cls):
def get_metadata(self):
"""
Retrieves metadata for the apply function. The description may be used
to advertise this action to the user. The parameters and their help
Expand All @@ -81,8 +80,11 @@ def get_metadata(cls):
:return: A FunctionMetadata object.
"""
data = FunctionMetadata.from_function(
cls.apply,
self.apply,
omit={'self', 'result', 'original_file_dict', 'file_diff_dict'})
data.name = cls.__name__
if hasattr(self, 'description'):
data.desc = self.description
data.name = self.__class__.__name__
data.id = id(self)

return data
Loading

0 comments on commit bcee2cd

Please sign in to comment.