-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cEP-0030: Next Generation Action System
Closes #181
- Loading branch information
1 parent
93c7cce
commit 1b18c5e
Showing
1 changed file
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
# Next Generation Action System | ||
|
||
| Metadata | | | ||
| -------- | -----------------------------------------------| | ||
| cEP | 30 | | ||
| Version | 1.0 | | ||
| Title | Next Generation Action System | | ||
| Authors | Akshat Karani <mailto:[email protected]> | | ||
| 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)]) | ||
|
||
``` |