From c151dd328af624c8427bea9054f9c3e4efd26556 Mon Sep 17 00:00:00 2001 From: akshatkarani Date: Sat, 11 May 2019 20:42:47 +0530 Subject: [PATCH] cEP-0030: Next Generation Action System Closes https://github.com/coala/cEPs/issues/181 --- cEP-0030.md | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 cEP-0030.md diff --git a/cEP-0030.md b/cEP-0030.md new file mode 100644 index 000000000..88f8092cb --- /dev/null +++ b/cEP-0030.md @@ -0,0 +1,211 @@ +# Next Generation Action System + +| Metadata | | +| -------- | -----------------------------------------------| +| cEP | 30 | +| Version | 1.0 | +| Title | Next Generation Action System | +| Authors | Akshat Karani | +| Status | Proposed | +| Type | Process | + +# Abstract + +This cEP describes the details about Next Generation Action System which +will allow bears to define their own actions as a part of +[GSoC'19 project](https://summerofcode.withgoogle.com/projects/#5450946933424128). + +# Introduction + +Bears run some analysis on a piece of code and output is in form of +a `Result` object. Then some action from a predefined set of actions +is applied to that `Result` object. This system is a bit restrictive +as action from predefined set of actionscan be taken. +If there is a system which will support bears defining their +own actions, then it will make bears more useful. +This project is about changing the current action system so that bears +can define their own actions. + + +# Implementation + +## Changing the `Result` class + +1. The first step is to facilitate bears defining their own actions. For this +the `__init__` and `classmethod` of the Result class need to be changes. +While yielding Result object a new optional parameter `actions` can be passed. +This is a tuple of actions that are specific to the bear. +2. Then a new attribute, `action` is added to the Result class. This is a +list of current default actions and actions that are specific to the bear. + +```python + +class Result: + + # A new parameter `actions` is added + def __init__(self, + origin, + message: str, + affected_code: (tuple, list) = (), + severity: int = RESULT_SEVERITY.NORMAL, + additional_info: str = '', + debug_msg='', + diffs: (dict, None) = None, + confidence: int = 100, + aspect: (aspectbase, None) = None, + message_arguments: dict = {}, + applied_actions: dict = {}, + actions: list = []): + + # A new attribute `actions` is added + self.actions = actions + # All the default actions are appended to `actions` + self.actions.append(ShowPatchAction(diffs)) + self.actions.append(ShowAppliedPatchesAction(applied_actions)) + self.actions.append(PrintMoreInfoAction(additional_info)) + self.actions.append(OpenEditorAction(affected_code)) + self.actions.append(IgnoreResultAction(origin, affected_code)) + self.actions.append(GeneratePatchesAction(diffs)) + self.actions.append(ApplyPatchAction(diffs)) + self.actions.append(PrintDebugMessageAction(debug_msg)) + +``` + +3. Now action objects contain all the relevant data, so we can remove +the unncessary fields from Result class like `diffs`, `additional_info`, +`applied_actions`, `debug_msg`. + +## Modifying the existing action classes + +1. Currently we need to pass result object as a parameter to method +of action classes. But now we have action objects defined inside result object, +the methods of actions classes are changed. +2. The changes made in `ResultAction` class are: + +```python + +class ResultAction: + + # `result` is removed as a parameter of the function + # It is no longer a `staticmethod` + def is_applicable(self, + original_file_dict, + file_diff_dict, + applied_actions=()): + return True + + # `result` is removed as a parameter of the function + def apply(self, original_file_dict, file_diff_dict, **kwargs): + raise NotImplementedError + + # `result` is removed as a parameter of the function + @enforce_signature + def apply_from_section(self, + original_file_dict: dict, + file_diff_dict: dict, + section: Section): + params = self.get_metadata().create_params_from_section(section) + # `result` is no longer passed as an argument + return self.apply(original_file_dict, file_diff_dict, **params) + +``` + +3. All the actions classes inherit from `ResultAction` and are changed +accordingly. For example here are the changes to +`PrintDebugMessageAction` class: + +```python + +class PrintDebugMessageAction(ResultAction): + + # `__init__` method is added with relevant parameters + def __init__(self, debug_msg): + self.debug_msg = debug_msg + + # `self.debug_msg` is used instead of `result.debug_msg` + @enforce_signature + def is_applicable(self, + original_file_dict, + file_diff_dict, + applied_actions=()): + if self.debug_msg != '': + return True + return 'There is no debug message.' + + # `self.debug_msg` is used instead of `result.debug_msg` + def apply(self, original_file_dict, file_diff_dict): + """ + Print (D)ebug message + """ + print(self.debug_msg) + + return file_diff_dict + +``` + +## Changing `ConsoleInteraction` and `Processing` module + +1. Now `ConsoleInteraction` and `Processing` module are changed +to use `result.actions`. +2. `acquire_actions_and_apply` function in `ConsoleInteraction` module is +changed. Instead of using `cli_actions`, we use `result.actions`. If a action +from `result.actions` is applicable then it is added to `metadata_list` +and then user is asked which action is to be applied from `metadata_list`. +3. `autoapply_actions` function in `Processing` module is modified +to use `result.actions`. + +## Writing bear specific actions + +1. With all the changes above it now possible to support bear specific actions. +2. Implementation of `AddNewLineAction` for `GitCommitBear`. +`AddNewLineAction` on application adds a new line between +shortlog and body of the commit message. + +```python + +from coalib.misc.Shell import run_shell_command +from coalib.results.result_actions.ResultAction import ResultAction + + +class AddNewLineAction(ResultAction): + + SUCCESS_MESSAGE = 'New Line added successfully.' + + def __init__(self, message, shortlog, body): + self.message = message + self.shortlog = shortlog + self.body = body + + def apply(self, original_file_dict, file_diff_dict, **kwargs): + """ + Add (N)ewline + """ + new_commit_message = '{}\n\n{}'.format(self.shortlog, self.body) + command = 'git commit --amend -m "{}"'.format(new_commit_message) + stdout, err = run_shell_command(command) + return file_diff_dict + +``` + +3. `CommitBear.py` file must also be changed so then when yielding +Result object, a list of actions is passed. + +```python + +yield Result(self, + message, + actions=[AddNewLineAction(message, shortlog, body)]) + +``` + +## Bears with Multiple Patches + +1. To support bears with multiple patches a new action `PatchAction` +is implemented. Corresponding to each patch provided for a problem we +have a instance of `PatchAction` in `result.actions`. +2. `PatchAction` is implemented such that it implicitly includes +`ShowPatchAction` and `ApplyPatchAction` for that patch. +3. Initially only `ShowPatchAction` of one patch is applied. +If user wants to apply this patch the `ApplyPatchAction` of that patch +is applied. If the user asks for another patch then `ShowPatchAction` +corresponding to that patch is applied.