From b66546fe30f53ac383d2778cda4cb9cd8bacfc9a Mon Sep 17 00:00:00 2001 From: guptash Date: Mon, 19 Apr 2021 21:56:42 -0400 Subject: [PATCH 01/22] Write skeleton code for classes.py --- dyc/base.py | 1 + dyc/classes.py | 254 ++++++++++++++++++++++++++++++++++++++ dyc/configs/defaults.yaml | 12 ++ dyc/dyc.py | 5 +- dyc/main.py | 14 ++- dyc/methods.py | 2 +- 6 files changed, 284 insertions(+), 4 deletions(-) create mode 100644 dyc/classes.py diff --git a/dyc/base.py b/dyc/base.py index 704b972..ec8c098 100644 --- a/dyc/base.py +++ b/dyc/base.py @@ -53,6 +53,7 @@ def initialize(self, change=None): lineno = pos + 1 i = i + 1 + if change and found: found = self._is_line_part_of_patches(lineno, line, patches) diff --git a/dyc/classes.py b/dyc/classes.py new file mode 100644 index 0000000..00e18f7 --- /dev/null +++ b/dyc/classes.py @@ -0,0 +1,254 @@ +import sys +import fileinput +import linecache +import click +import os +import re +from .utils import get_leading_whitespace, add_start_end, is_comment, is_one_line_method +from .base import Builder +from .methods import ArgumentDetails + + +class ClassBuilder(Builder): + already_printed_filepaths = [] # list of already printed files + + def validate(self, result): + """ + An abstract validator method that checks if the method is + still valid and gives the final decision + Parameters + ---------- + MethodInterface result: The Method Interface result + """ + if not result: + return False + name = result.name + if name not in self.config.get( + "ignore", [] + ) and not self.is_first_line_documented(result): + if ( + self.filename not in self.already_printed_filepaths + ): # Print file of method to document + click.echo( + "\n\nIn file {} :\n".format( + click.style( + os.path.join(*self.filename.split(os.sep)[-3:]), fg="red" + ) + ) + ) + self.already_printed_filepaths.append(self.filename) + confirmed = ( + True + if self.placeholders + else click.confirm( + "Do you want to document class {}?".format( + click.style(name, fg="green") + ) + ) + ) + if confirmed: + return True + + return False + + def is_first_line_documented(self, result): + """ + A boolean function that determines weather the first line has + a docstring or not + Parameters + ---------- + MethodInterface result: Is a method interface class that could be + subject to be taking a docstring + str line: The line of the found method + """ + returned = False + # print(result.start, ' ', result.end) + # for x in range(result.start, result.end): + # line = linecache.getline(result.filename, x) + # if self.config.get("open") in line: + # returned = True + # break + + # copied from pull request + read_first_line=linecache.getline(result.filename, result.start) + read_second_line=linecache.getline(result.filename, result.start+1) + finalTwoLines=read_first_line+"\n"+read_second_line + # 1. Changed long regex since the assumption is that the line already has the keyword (e.g. def, class) in it + # 2. Can use a simpler regex + # 3. Potentially replace """ with config[class][open] as value + pattern = r':[\s\S]?[\n][\s]*(""")' + match = re.search(pattern,finalTwoLines) + returned = True if match else False + + linecache.clearcache() + return returned + + def extract_and_set_information(self, filename, start, line, length): + """ + This is a main abstract method tin the builder base + to add result into details. Used in Method Builder to + pull the candidates that are subject to docstrings + Parameters + ---------- + str filename: The file's name + int start: Starting line + str line: Full line text + int length: The length of the extracted data + """ + #------------------------------# + # CAN GENERALIZE THIS FUNCTION # + #------------------------------# + start_line = linecache.getline(filename, start) + initial_line = line + start_leading_space = get_leading_whitespace( + start_line + ) # Where function started + class_string = start_line + if not is_one_line_method(start_line, self.config.get("keywords")): + class_string = line + linesBackwards = class_string.count("\n") - 1 + start_leading_space = get_leading_whitespace( + linecache.getline(filename, start - linesBackwards) + ) + line_within_scope = True + lineno = start + 1 + line = linecache.getline(filename, lineno) + end_of_file = False + end = None + while line_within_scope and not end_of_file: + current_leading_space = get_leading_whitespace(line) + if len(current_leading_space) <= len(start_leading_space) and line.strip(): + end = lineno - 1 + break + class_string += line + lineno = lineno + 1 + line = linecache.getline(filename, int(lineno)) + end_of_file = True if lineno > length else False + + if not end: + end = length + + linecache.clearcache() + return ClassInterface( + plain=class_string, + name=self._get_name(initial_line), + start=start, + end = end, + filename = filename, + arguments = self.extract_arguments(initial_line.strip("\n")), + config = self.config, + leading_space=get_leading_whitespace(initial_line), + placeholders=self.placeholders + ) + + def extract_arguments(self, line): + """ + Public extract argument method that calls ArgumentDetails + class to extract args + Parameters + ---------- + """ + args = ArgumentDetails(line, self.config.get("arguments", {})) + args.extract() + return args.sanitize() + + def prompts(self): + """ + Abstract prompt method in builder to execute prompts over candidates + """ + # Do a prompt for each class interface + print("SELF.DETAILS: ", self.details) + + for class_interface in self._class_interface_gen(): + class_interface.prompt() if class_interface else None + + + def _class_interface_gen(self): + # For each ClassInterface object in details[filename][class_name] + pass + + def apply(self): + # For each ClassInterface object (_class_interface_gen again for this) + # Essentially the same as methodbuilder's apply + pass + + def _get_name(self, line): + """ + Grabs the name of the method from the given line + Parameters + ---------- + str line: String line that has the method's name + """ + for keyword in self.config.get("keywords", []): + clear_defs = re.sub("{} ".format(keyword), "", line.strip()) + name = re.sub(r"\([^)]*\)\:", "", clear_defs).strip() + if re.search(r"\(([\s\S]*)\)", name): + try: + name = re.match(r"^[^\(]+", name).group() + except: + pass + if name: + return name + + # Dumb duplicate of _is_class + def _is_class(self, line): + """ + A predicate method that checks if a line is a + class + + Parameters + ---------- + str line: Text string of a line in a file + """ + return line.strip().split(" ")[0] in self.config.get("keywords") + + + +class ClassFormatter: + + def format(self): + # Gonna have to look at how MethodFormatter does it + # Basically the same as that, tho, as the docstrings should be formatted the same + pass + + +class ClassInterface(ClassFormatter): + def __init__( + self, + plain, + name, + start, + end, + filename, + arguments, + config, + leading_space, + placeholders, + ): + self.plain = plain + self.name = name + self.start = start + self.end = end + self.filename = filename + self.arguments = arguments + self.class_docstring = "" + self.arg_docstring = [] + self.config = config + self.leading_space = leading_space + self.placeholders = placeholders + + def prompt(self): + # Wrapper method for prompting user related to the Class + self._prompt_docstring() + self._prompt_args() + self.format() + + def _prompt_docstring(self): + # Prompt user for main docstring title of class + pass + + def _prompt_args(self): + # Prompting for arguements (look at MethodInterface class) + pass + + diff --git a/dyc/configs/defaults.yaml b/dyc/configs/defaults.yaml index a119aec..0f907a5 100644 --- a/dyc/configs/defaults.yaml +++ b/dyc/configs/defaults.yaml @@ -36,3 +36,15 @@ formats: - self class: keywords: ['class'] + enabled: true + indent: '4 spaces' + indent_content: false + open: '"""' + close: '"""' + comments: + - '#' + break_after_open: true + break_after_docstring: true + break_before_close: true + words_per_line: 10 + within_scope: true diff --git a/dyc/dyc.py b/dyc/dyc.py index 5e97a6b..d11bf40 100644 --- a/dyc/dyc.py +++ b/dyc/dyc.py @@ -46,8 +46,9 @@ def start(config, files, placeholders): config.plain['file_list'] = list(files) dyc = DYC(config.plain, placeholders=placeholders) dyc.prepare() - dyc.process_methods() - dyc.process_top() + # dyc.process_methods() + # dyc.process_top() + dyc.process_classes() @main.command() diff --git a/dyc/main.py b/dyc/main.py index c80c7c1..426cfa8 100644 --- a/dyc/main.py +++ b/dyc/main.py @@ -9,6 +9,7 @@ from .utils import get_extension from .methods import MethodBuilder from .top import TopBuilder +from .classes import ClassBuilder from .base import Processor @@ -54,8 +55,19 @@ def process_methods(self, diff_only=False, changes=[]): def process_classes(self): """ - Main method that documents Classes in a file. Still TODO + Main method that documents Classes in a file. """ + print("\nProcessing Classes\n\r") + for filename in self.file_list: + extension = get_extension(filename) + fmt = self.formats.get(extension) + classes_cnf = fmt.get("class", {}) + classes_cnf["arguments"] = fmt.get("arguments") + builder = ClassBuilder(filename, classes_cnf, placeholders=self.placeholders) + builder.initialize() + builder.prompts() + # builder.apply() + # builder.clear(filename) # self.classes = ClassesBuilder() pass diff --git a/dyc/methods.py b/dyc/methods.py index 70cdefc..3df03ae 100644 --- a/dyc/methods.py +++ b/dyc/methods.py @@ -43,7 +43,7 @@ def extract_and_set_information(self, filename, start, line, length): method_string = start_line if not is_one_line_method(start_line, self.config.get("keywords")): method_string = line - linesBackwards = method_string.count("\n") - 1 + linesBackwards = method_string.count("\n") - 1 start_leading_space = get_leading_whitespace( linecache.getline(filename, start - linesBackwards) ) From 21c23ef44191be839529c52670f9d893c3329255 Mon Sep 17 00:00:00 2001 From: guptash Date: Thu, 22 Apr 2021 23:08:22 -0400 Subject: [PATCH 02/22] Black formatting commit --- dyc/base.py | 3 +-- dyc/configs/__init__.py | 18 +++++++++--------- dyc/diff.py | 36 ++++++++++++++++++------------------ dyc/dyc.py | 16 ++++++++-------- dyc/events.py | 10 +++++----- dyc/methods.py | 2 +- dyc/parser.py | 4 ++-- dyc/utils.py | 4 ++-- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/dyc/base.py b/dyc/base.py index ec8c098..5f97f30 100644 --- a/dyc/base.py +++ b/dyc/base.py @@ -35,7 +35,7 @@ def initialize(self, change=None): word.lstrip() for word in line.split(" ") if word.lstrip() in keywords ] found = len(foundList) > 0 and not is_comment( - line, self.config.get('comments') + line, self.config.get("comments") ) # Checking an unusual format in method declaration if foundList: @@ -53,7 +53,6 @@ def initialize(self, change=None): lineno = pos + 1 i = i + 1 - if change and found: found = self._is_line_part_of_patches(lineno, line, patches) diff --git a/dyc/configs/__init__.py b/dyc/configs/__init__.py index ad7acf7..b7d5554 100644 --- a/dyc/configs/__init__.py +++ b/dyc/configs/__init__.py @@ -2,8 +2,8 @@ from ..utils import read_yaml ROOT_PATH = os.getcwd() -DEFAULT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaults.yaml') -CUSTOM = os.path.join(ROOT_PATH, 'dyc.yaml') +DEFAULT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "defaults.yaml") +CUSTOM = os.path.join(ROOT_PATH, "dyc.yaml") class Config(object): @@ -13,7 +13,7 @@ class Config(object): def override(self): """ - Entry point to Config, mainly the + Entry point to Config, mainly the """ self._override_basic() self._override_formats() @@ -31,27 +31,27 @@ def _override_formats(self): Loops over the formats i.e `py`, `js`. And assigns the given customized values in Root dyc.yaml file. Otherwise it will use the default values """ - formats = self.custom.get('formats', []) + formats = self.custom.get("formats", []) for index, value in enumerate(formats): - extension = value.get('extension') + extension = value.get("extension") cnf_index = self._get_custom_extension_index(extension) try: for nested_key, nested_obj in value.iteritems(): try: - self.plain.get('formats')[cnf_index][nested_key].update( + self.plain.get("formats")[cnf_index][nested_key].update( **nested_obj ) if nested_obj else None except AttributeError: continue except (IndexError, TypeError): - self.plain.get('formats').append(value) + self.plain.get("formats").append(value) def _get_custom_extension_index(self, extension): """ If a customised extension is defined. Add that to the config """ - for index, value in enumerate(self.plain.get('formats')): - if value.get('extension') == extension: + for index, value in enumerate(self.plain.get("formats")): + if value.get("extension") == extension: return index def _is_mutated(self, value): diff --git a/dyc/diff.py b/dyc/diff.py index 6513ec0..ba5f9cc 100644 --- a/dyc/diff.py +++ b/dyc/diff.py @@ -15,7 +15,7 @@ class DiffParser: - PREFIX = 'diff --git' + PREFIX = "diff --git" def parse(self, staged=False): """ @@ -25,8 +25,8 @@ def parse(self, staged=False): ---------- bool staged: Only using the staged files """ - self.diffs = self.repo.index.diff('HEAD' if staged else None) - self.plain = self.repo.git.diff('HEAD').split('\n') + self.diffs = self.repo.index.diff("HEAD" if staged else None) + self.plain = self.repo.git.diff("HEAD").split("\n") return self._pack() def _pack(self): @@ -36,9 +36,9 @@ def _pack(self): patches = [] for diff in self.diffs: if not self.is_candidate(diff.a_path): - print('File {} is not a candidate to apply DYC'.format(diff.a_path)) + print("File {} is not a candidate to apply DYC".format(diff.a_path)) continue - sep = '{} a/{} b/{}'.format(self.PREFIX, diff.a_path, diff.b_path) + sep = "{} a/{} b/{}".format(self.PREFIX, diff.a_path, diff.b_path) patch = self.__clean(self.__patch(sep), diff) patches.append(patch) return patches @@ -72,7 +72,7 @@ def __patch(self, separator): break elif hit: patch.append(line) - return '\n'.join(patch) + return "\n".join(patch) def __pack(self, patch): """ @@ -90,14 +90,14 @@ def __pack(self, patch): _hunk = get_hunk(line) if (len(patch) - 1) == index: - final.append(dict(patch='\n'.join(result), hunk=(start, end))) + final.append(dict(patch="\n".join(result), hunk=(start, end))) if len(_hunk) and not hit: start, end = get_additions_in_first_hunk(get_hunk(line)) hit = True continue elif len(_hunk) and hit: - final.append(dict(patch='\n'.join(result), hunk=(start, end))) + final.append(dict(patch="\n".join(result), hunk=(start, end))) start, end = get_additions_in_first_hunk(get_hunk(line)) result = [] hit = True @@ -109,13 +109,13 @@ def __pack(self, patch): def __clean(self, patch, diff): """Returns a clean dict of a path""" result = {} - result['additions'] = self.__additions( - self.__pack(patch.split('\n')), diff.a_path + result["additions"] = self.__additions( + self.__pack(patch.split("\n")), diff.a_path ) # [{hunk: (start, end), patch:}] - result['plain'] = patch - result['diff'] = diff - result['name'] = ntpath.basename(diff.a_path) - result['path'] = diff.a_path + result["plain"] = patch + result["diff"] = diff + result["name"] = ntpath.basename(diff.a_path) + result["path"] = diff.a_path return result def __additions(self, hunks, path): @@ -127,17 +127,17 @@ def __additions(self, hunks, path): str path: path of a file """ for hunk in hunks: - patch = hunk.get('patch') + patch = hunk.get("patch") result = [] - for line in patch.split('\n'): + for line in patch.split("\n"): try: - if line[0] == '+' and not line.startswith('+++'): + if line[0] == "+" and not line.startswith("+++"): l = line[1:] result.append(l) except IndexError as e: print(e.message) continue - hunk['patch'] = '\n'.join(result) + hunk["patch"] = "\n".join(result) return hunks diff --git a/dyc/dyc.py b/dyc/dyc.py index d11bf40..5e45165 100644 --- a/dyc/dyc.py +++ b/dyc/dyc.py @@ -24,7 +24,7 @@ def main(config): """ The entering method to the CLI - Parameters + Parameterss ---------- ConfigParser config: Config going to be used in DYC """ @@ -32,8 +32,8 @@ def main(config): @main.command() -@click.option('--placeholders', is_flag=True, default=False) -@click.argument('files', nargs=-1, type=click.Path(exists=True), required=False) +@click.option("--placeholders", is_flag=True, default=False) +@click.argument("files", nargs=-1, type=click.Path(exists=True), required=False) @config def start(config, files, placeholders): """ @@ -43,17 +43,17 @@ def start(config, files, placeholders): over and add missing documentation on. """ if files: - config.plain['file_list'] = list(files) + config.plain["file_list"] = list(files) dyc = DYC(config.plain, placeholders=placeholders) dyc.prepare() - # dyc.process_methods() - # dyc.process_top() + dyc.process_methods() + dyc.process_top() dyc.process_classes() @main.command() @click.option( - '--watch', help='Add default placeholder when watching', is_flag=True, default=False + "--watch", help="Add default placeholder when watching", is_flag=True, default=False ) @config def diff(config, watch): @@ -65,7 +65,7 @@ def diff(config, watch): else: diff = Diff(config.plain) uncommitted = diff.uncommitted - paths = [idx.get('path') for idx in uncommitted] + paths = [idx.get("path") for idx in uncommitted] if len(uncommitted): dyc = DYC(config.plain) dyc.prepare(files=paths) diff --git a/dyc/events.py b/dyc/events.py index aefa67f..e39fbe7 100644 --- a/dyc/events.py +++ b/dyc/events.py @@ -27,14 +27,14 @@ def dispatch(self, event): diff = Diff(self.config.plain) uncommitted = diff.uncommitted paths = [ - idx.get('path') + idx.get("path") for idx in uncommitted - if './{}'.format(idx.get('path')) == event.src_path + if "./{}".format(idx.get("path")) == event.src_path ] filtered = [ idx for idx in uncommitted - if './{}'.format(idx.get('path')) == event.src_path + if "./{}".format(idx.get("path")) == event.src_path ] if len(filtered): dyc = DYC(self.config.plain, placeholders=True) @@ -57,12 +57,12 @@ def start(cls, config): observer = Observer() event_handler = WatchEvent() event_handler.config = config - observer.schedule(event_handler, '.', recursive=True) + observer.schedule(event_handler, ".", recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() - print('Quitting..') + print("Quitting..") observer.join() diff --git a/dyc/methods.py b/dyc/methods.py index 3df03ae..70cdefc 100644 --- a/dyc/methods.py +++ b/dyc/methods.py @@ -43,7 +43,7 @@ def extract_and_set_information(self, filename, start, line, length): method_string = start_line if not is_one_line_method(start_line, self.config.get("keywords")): method_string = line - linesBackwards = method_string.count("\n") - 1 + linesBackwards = method_string.count("\n") - 1 start_leading_space = get_leading_whitespace( linecache.getline(filename, start - linesBackwards) ) diff --git a/dyc/parser.py b/dyc/parser.py index b8b6558..3e75bba 100644 --- a/dyc/parser.py +++ b/dyc/parser.py @@ -17,7 +17,7 @@ def __init__(self): except AttributeError: click.echo( click.style( - '`dyc.yaml` Missing or Incorrectly formatted. USING default settings', - fg='cyan', + "`dyc.yaml` Missing or Incorrectly formatted. USING default settings", + fg="cyan", ) ) diff --git a/dyc/utils.py b/dyc/utils.py index 39a48ca..856708e 100644 --- a/dyc/utils.py +++ b/dyc/utils.py @@ -164,7 +164,7 @@ def get_additions_in_first_hunk(hunk): def is_one_line_method(line, keywords): """ - Gets True if the line holds a complete method declaration (from 'def to :), + Gets True if the line holds a complete method declaration (from 'def to :), otherwise it gets False ---------- str line: Text line @@ -186,4 +186,4 @@ def is_comment(line, comments): str line: The line string in a file list comments: A list of potential comment keywords """ - return line.lstrip(' ')[0] in comments + return line.lstrip(" ")[0] in comments From c41580b865b9e545bfd51d4e8834d59af6b39d57 Mon Sep 17 00:00:00 2001 From: guptash Date: Thu, 22 Apr 2021 23:09:55 -0400 Subject: [PATCH 03/22] Finished initial implementation of ClassBuilder class --- dyc/classes.py | 386 ++++++++++++++++++++++++++++++++------ dyc/configs/defaults.yaml | 6 + dyc/main.py | 12 +- 3 files changed, 336 insertions(+), 68 deletions(-) diff --git a/dyc/classes.py b/dyc/classes.py index 00e18f7..cd609b8 100644 --- a/dyc/classes.py +++ b/dyc/classes.py @@ -4,9 +4,16 @@ import click import os import re -from .utils import get_leading_whitespace, add_start_end, is_comment, is_one_line_method +import copy +from .utils import ( + get_leading_whitespace, + add_start_end, + is_comment, + is_one_line_method, + BlankFormatter, + get_indent, +) from .base import Builder -from .methods import ArgumentDetails class ClassBuilder(Builder): @@ -14,11 +21,11 @@ class ClassBuilder(Builder): def validate(self, result): """ - An abstract validator method that checks if the method is + An abstract validator method that checks if the class is still valid and gives the final decision Parameters ---------- - MethodInterface result: The Method Interface result + ClassInterface result: The Class Interface result """ if not result: return False @@ -28,7 +35,7 @@ def validate(self, result): ) and not self.is_first_line_documented(result): if ( self.filename not in self.already_printed_filepaths - ): # Print file of method to document + ): # Print file of class to document click.echo( "\n\nIn file {} :\n".format( click.style( @@ -50,34 +57,32 @@ def validate(self, result): return True return False - + def is_first_line_documented(self, result): """ A boolean function that determines weather the first line has a docstring or not Parameters ---------- - MethodInterface result: Is a method interface class that could be + ClassInterface result: Is a class interface class that could be subject to be taking a docstring - str line: The line of the found method + str line: The line of the found class """ returned = False - # print(result.start, ' ', result.end) - # for x in range(result.start, result.end): - # line = linecache.getline(result.filename, x) - # if self.config.get("open") in line: - # returned = True - # break - - # copied from pull request - read_first_line=linecache.getline(result.filename, result.start) - read_second_line=linecache.getline(result.filename, result.start+1) - finalTwoLines=read_first_line+"\n"+read_second_line + # copied from pull request related to issue #63 + read_first_line = linecache.getline(result.filename, result.start) + read_second_line = linecache.getline(result.filename, result.start + 1) + finalTwoLines = read_first_line + "\n" + read_second_line # 1. Changed long regex since the assumption is that the line already has the keyword (e.g. def, class) in it # 2. Can use a simpler regex # 3. Potentially replace """ with config[class][open] as value - pattern = r':[\s\S]?[\n][\s]*(""")' - match = re.search(pattern,finalTwoLines) + + # The open_brace_pattern is """ by default, but can be configured in the yml files + open_brace_pattern = self.config.get("open", None) + pattern = r":\n\s*?({})".format(open_brace_pattern) + # Other experimental regexe patterns below + #:\n\s{4}(?:""") #r':[\s\S]?[\n][\s]*(""")' + match = re.search(pattern, finalTwoLines) returned = True if match else False linecache.clearcache() @@ -86,7 +91,7 @@ def is_first_line_documented(self, result): def extract_and_set_information(self, filename, start, line, length): """ This is a main abstract method tin the builder base - to add result into details. Used in Method Builder to + to add result into details. Used in Class Builder to pull the candidates that are subject to docstrings Parameters ---------- @@ -95,9 +100,9 @@ def extract_and_set_information(self, filename, start, line, length): str line: Full line text int length: The length of the extracted data """ - #------------------------------# + # ------------------------------# # CAN GENERALIZE THIS FUNCTION # - #------------------------------# + # ------------------------------# start_line = linecache.getline(filename, start) initial_line = line start_leading_space = get_leading_whitespace( @@ -106,7 +111,7 @@ def extract_and_set_information(self, filename, start, line, length): class_string = start_line if not is_one_line_method(start_line, self.config.get("keywords")): class_string = line - linesBackwards = class_string.count("\n") - 1 + linesBackwards = class_string.count("\n") - 1 start_leading_space = get_leading_whitespace( linecache.getline(filename, start - linesBackwards) ) @@ -133,51 +138,83 @@ def extract_and_set_information(self, filename, start, line, length): plain=class_string, name=self._get_name(initial_line), start=start, - end = end, - filename = filename, - arguments = self.extract_arguments(initial_line.strip("\n")), - config = self.config, + end=end, + filename=filename, + classes=self.extract_classes(initial_line.strip("\n")), + config=self.config, leading_space=get_leading_whitespace(initial_line), - placeholders=self.placeholders + placeholders=self.placeholders, ) - def extract_arguments(self, line): + def extract_classes(self, line): """ - Public extract argument method that calls ArgumentDetails - class to extract args + Public method to extract parents that calls ClassDetails + class to extract parents Parameters ---------- + str line: line that contains the parents of the class """ - args = ArgumentDetails(line, self.config.get("arguments", {})) - args.extract() - return args.sanitize() - + parents = ClassDetails(line, self.config.get("parents", {})) + parents.extract() + return parents.sanitize() + def prompts(self): """ Abstract prompt method in builder to execute prompts over candidates """ - # Do a prompt for each class interface - print("SELF.DETAILS: ", self.details) - for class_interface in self._class_interface_gen(): class_interface.prompt() if class_interface else None - def _class_interface_gen(self): # For each ClassInterface object in details[filename][class_name] pass + if not self.details: + yield None + for filename, func_pack in self.details.items(): + for class_interface in func_pack.values(): + yield class_interface def apply(self): - # For each ClassInterface object (_class_interface_gen again for this) - # Essentially the same as methodbuilder's apply - pass + """ + Over here we are looping over the result of the + chosen classes to document and applying the changes to the + files as confirmed + """ + for class_interface in self._class_interface_gen(): + if not class_interface: + continue + fileInput = fileinput.input(class_interface.filename, inplace=True) + + for line in fileInput: + tmpLine = line + if self._is_class(line) and ":" not in line: + openedP = line.count("(") + closedP = line.count(")") + pos = 1 + if openedP == closedP: + continue + else: + while openedP != closedP: + tmpLine += fileInput.readline() + openedP = tmpLine.count("(") + closedP = tmpLine.count(")") + pos += 1 + line = tmpLine + + if self._get_name(line) == class_interface.name: + if self.config.get("within_scope"): + sys.stdout.write(line + class_interface.result + "\n") + else: + sys.stdout.write(class_interface.result + "\n" + line) + else: + sys.stdout.write(line) def _get_name(self, line): """ - Grabs the name of the method from the given line + Grabs the name of the class from the given line Parameters ---------- - str line: String line that has the method's name + str line: String line that has the class' name """ for keyword in self.config.get("keywords", []): clear_defs = re.sub("{} ".format(keyword), "", line.strip()) @@ -203,13 +240,184 @@ def _is_class(self, line): return line.strip().split(" ")[0] in self.config.get("keywords") - class ClassFormatter: + formatted_string = "{open}{break_after_open}{class_docstring}{break_after_docstring}{empty_line}{parents_format}{break_before_close}{close}" + fmt = BlankFormatter() + def format(self): - # Gonna have to look at how MethodFormatter does it - # Basically the same as that, tho, as the docstrings should be formatted the same - pass + """ + Public formatting method that executes a pattern of methods to + complete the process + """ + self.pre() + self.build_docstrings() + self.build_parents() + self.result = self.fmt.format(self.formatted_string, **self.class_format) + self.add_indentation() + self.polish() + + def wrap_strings(self, words): + """ + Compact how many words should be in a line + Parameters + ---------- + str words: docstring given + """ + subs = [] + n = self.config.get("words_per_line") + for i in range(0, len(words), n): + subs.append(" ".join(words[i : i + n])) + return "\n".join(subs) + + def pre(self): + """ + In the formatter, this method sets up the object that + will be used in a formatted way,. Also translates configs + into consumable values + """ + class_format = copy.deepcopy(self.config) + class_format["indent"] = ( + get_indent(class_format["indent"]) if class_format["indent"] else " " + ) + class_format["indent_content"] = ( + get_indent(class_format["indent"]) + if get_indent(class_format["indent_content"]) + else "" + ) + class_format["break_after_open"] = ( + "\n" if class_format["break_after_open"] else "" + ) + class_format["break_after_docstring"] = ( + "\n" if class_format["break_after_docstring"] else "" + ) + class_format["break_before_close"] = ( + "\n" if class_format["break_before_close"] else "" + ) + class_format["empty_line"] = "\n" + + parents_format = copy.deepcopy(self.config.get("parents")) + parents_format["inline"] = "" if parents_format["inline"] else "\n" + + self.class_format = class_format + self.parents_format = parents_format + + def build_docstrings(self): + """ + Mainly adds docstrings of the class after cleaning up text + into reasonable chunks + """ + text = self.class_docstring or "Missing Docstring!" + self.class_format["class_docstring"] = self.wrap_strings(text.split(" ")) + + def build_parents(self): + """ + Main function for wrapping up parent docstrings + """ + if not self.classes: # if len(self.classes) == 0 + self.class_format["parents_format"] = "" + self.class_format["break_before_close"] = "" + self.class_format["empty_line"] = "" + return + + config = self.config.get("parents") + formatted_parents = "{prefix} {name}: {doc}" + + title = self.parents_format.get("title") + if title: + underline = "-" * len(title) + self.parents_format["title"] = ( + "{}\n{}\n".format(title, underline) + if config.get("underline") + else "{}\n".format(title) + ) + + result = [] + + if self.classes: # if len(self.classes) > 0 + for parent_details in self.class_parent_list: + parent_details["prefix"] = self.parents_format.get("prefix") + + result.append( + self.fmt.format(formatted_parents, **parent_details).strip() + ) + + self.parents_format["body"] = "\n".join(result) + self.class_format["parents_format"] = self.fmt.format( + "{title}{body}", **self.parents_format + ) + + def add_indentation(self): + """ + Translates indent params to actual indents + """ + temp = self.result.split("\n") + space = self.class_format.get("indent") + indent_content = self.class_format.get("indent_content") + if indent_content: + content = temp[1:-1] + content = [indent_content + docline for docline in temp][1:-1] + temp[1:-1] = content + self.result = "\n".join([space + docline for docline in temp]) + + def confirm(self, polished): + """ + Pop up editor function to finally confirm if the documented + format is accepted + Parameters + ---------- + str polished: complete polished string before popping up + """ + polished = add_start_end(polished) + class_split = self.plain.split("\n") + if self.config.get("within_scope"): + # Check if class comes in an unusual format + keywords = self.config.get("keywords") + firstLine = class_split[0] + pos = 1 + while not is_one_line_method(firstLine, keywords): + firstLine += class_split[pos] + pos += 1 + class_split.insert(pos, polished) + else: + class_split.insert(0, polished) + + try: + result = "\n".join(class_split) + message = click.edit( + "## CONFIRM: MODIFY DOCSTRING BETWEEN START AND END LINES ONLY\n\n" + + result + ) + message = result if message == None else "\n".join(message.split("\n")[2:]) + except: + print("Quitting the program in the editor terminates the process. Thanks") + sys.exit() + + final = [] + start = False + end = False + + for x in message.split("\n"): + stripped = x.strip() + if stripped == "## END": + end = True + if start and not end: + final.append(x) + if stripped == "## START": + start = True + + self.result = "\n".join(final) + + def polish(self): + """ + Editor wrapper to confirm result + """ + docstring = self.result.split("\n") + polished = "\n".join([self.leading_space + docline for docline in docstring]) + if self.placeholders: + self.result = polished + else: + self.confirm(polished) class ClassInterface(ClassFormatter): @@ -220,7 +428,7 @@ def __init__( start, end, filename, - arguments, + classes, config, leading_space, placeholders, @@ -230,25 +438,79 @@ def __init__( self.start = start self.end = end self.filename = filename - self.arguments = arguments + self.classes = classes self.class_docstring = "" - self.arg_docstring = [] + self.class_parent_list = [] self.config = config self.leading_space = leading_space self.placeholders = placeholders def prompt(self): - # Wrapper method for prompting user related to the Class self._prompt_docstring() - self._prompt_args() + self._prompt_parents() self.format() - + def _prompt_docstring(self): - # Prompt user for main docstring title of class - pass - - def _prompt_args(self): - # Prompting for arguements (look at MethodInterface class) - pass + """ + Simple prompt for a class' docstring + """ + if self.placeholders: + self.class_docstring = "" + else: + echo_name = click.style(self.name, fg="green") + self.class_docstring = click.prompt( + "\n({}) Class docstring ".format(echo_name) + ) + + def _prompt_parents(self): + """ + Wrapper for classes + """ + + def _echo_parent_style(parent): + """ + Just a small wrapper for echoing parentss + Parameters + ----------- + str parent: parent name + """ + return click.style("{}".format(parent), fg="red") + + for parent in self.classes: + doc_placeholder = "" + parent_doc = ( + click.prompt( + "\n({}) Inherited class docstring ".format( + _echo_parent_style(parent) + ) + ) + if not self.placeholders + else doc_placeholder + ) + self.class_parent_list.append(dict(doc=parent_doc, name=parent)) + + +class ClassDetails(object): + def __init__(self, line, config): + self.line = line + self.config = config + self.parents = [] + def extract(self): + """ + Retrieves class parents from a line and cleans them + """ + try: + parents = re.search(r"\(([\s\S]*)\)", self.line).group(1).split(",") + self.parents = [ + parent.replace("\n", "").replace("\t", "").strip() for parent in parents + ] + except: + pass + self.parents = [parent for parent in self.parents if parent != ''] + def sanitize(self): + """ + Sanitizes classes to validate all classes are correct + """ + return map(lambda parent: re.findall(r"[a-zA-Z0-9_]+", parent)[0], self.parents) diff --git a/dyc/configs/defaults.yaml b/dyc/configs/defaults.yaml index 0f907a5..19e1e0e 100644 --- a/dyc/configs/defaults.yaml +++ b/dyc/configs/defaults.yaml @@ -48,3 +48,9 @@ formats: break_before_close: true words_per_line: 10 within_scope: true + parents: + title: 'Inheritance' + underline: true + add_type: false + inline: false + prefix: '' diff --git a/dyc/main.py b/dyc/main.py index 426cfa8..0cac4b1 100644 --- a/dyc/main.py +++ b/dyc/main.py @@ -62,14 +62,14 @@ def process_classes(self): extension = get_extension(filename) fmt = self.formats.get(extension) classes_cnf = fmt.get("class", {}) - classes_cnf["arguments"] = fmt.get("arguments") - builder = ClassBuilder(filename, classes_cnf, placeholders=self.placeholders) + classes_cnf["parents"] = fmt.get("parents") + builder = ClassBuilder( + filename, classes_cnf, placeholders=self.placeholders + ) builder.initialize() builder.prompts() - # builder.apply() - # builder.clear(filename) - # self.classes = ClassesBuilder() - pass + builder.apply() + builder.clear(filename) def process_top(self, diff_only=False): """ From ae9e5b247caa89b7935fa58f711061d82d381055 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 21:21:10 -0400 Subject: [PATCH 04/22] add --test flag for test suite and debugging purposes --- dyc/base.py | 3 ++- dyc/classes.py | 33 ++++++++++++++++++++++----------- dyc/dyc.py | 5 +++-- dyc/main.py | 9 +++++---- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/dyc/base.py b/dyc/base.py index 5f97f30..8d2acff 100644 --- a/dyc/base.py +++ b/dyc/base.py @@ -6,10 +6,11 @@ class Builder(object): - def __init__(self, filename, config, placeholders=False): + def __init__(self, filename, config, placeholders=False, test=False): self.filename = filename self.config = config self.placeholders = placeholders + self.test = test details = dict() diff --git a/dyc/classes.py b/dyc/classes.py index cd609b8..dfafd81 100644 --- a/dyc/classes.py +++ b/dyc/classes.py @@ -144,6 +144,7 @@ def extract_and_set_information(self, filename, start, line, length): config=self.config, leading_space=get_leading_whitespace(initial_line), placeholders=self.placeholders, + test=self.test ) def extract_classes(self, line): @@ -167,7 +168,6 @@ def prompts(self): def _class_interface_gen(self): # For each ClassInterface object in details[filename][class_name] - pass if not self.details: yield None for filename, func_pack in self.details.items(): @@ -227,7 +227,6 @@ def _get_name(self, line): if name: return name - # Dumb duplicate of _is_class def _is_class(self, line): """ A predicate method that checks if a line is a @@ -245,7 +244,7 @@ class ClassFormatter: formatted_string = "{open}{break_after_open}{class_docstring}{break_after_docstring}{empty_line}{parents_format}{break_before_close}{close}" fmt = BlankFormatter() - def format(self): + def format(self, test): """ Public formatting method that executes a pattern of methods to complete the process @@ -256,6 +255,7 @@ def format(self): self.result = self.fmt.format(self.formatted_string, **self.class_format) self.add_indentation() self.polish() + self.test = test def wrap_strings(self, words): """ @@ -384,11 +384,16 @@ def confirm(self, polished): try: result = "\n".join(class_split) - message = click.edit( - "## CONFIRM: MODIFY DOCSTRING BETWEEN START AND END LINES ONLY\n\n" - + result - ) - message = result if message == None else "\n".join(message.split("\n")[2:]) + + # If running an automated test + if self.test: + message = result + else: + message = click.edit( + "## CONFIRM: MODIFY DOCSTRING BETWEEN START AND END LINES ONLY\n\n" + + result + ) + message = result if message == None else "\n".join(message.split("\n")[2:]) except: print("Quitting the program in the editor terminates the process. Thanks") sys.exit() @@ -432,6 +437,7 @@ def __init__( config, leading_space, placeholders, + test, ): self.plain = plain self.name = name @@ -444,11 +450,12 @@ def __init__( self.config = config self.leading_space = leading_space self.placeholders = placeholders + self.test = test def prompt(self): self._prompt_docstring() self._prompt_parents() - self.format() + self.format(test=self.test) def _prompt_docstring(self): """ @@ -507,10 +514,14 @@ def extract(self): ] except: pass - self.parents = [parent for parent in self.parents if parent != ''] + self.parents = [parent for parent in self.parents if parent != ""] def sanitize(self): """ Sanitizes classes to validate all classes are correct """ - return map(lambda parent: re.findall(r"[a-zA-Z0-9_]+", parent)[0], self.parents) + # Updated filter function to remove invalid parent names + return list(filter( + lambda parent: not re.findall(r"[^a-zA-Z0-9_]", parent), + self.parents + )) diff --git a/dyc/dyc.py b/dyc/dyc.py index 5e45165..42b8df4 100644 --- a/dyc/dyc.py +++ b/dyc/dyc.py @@ -34,8 +34,9 @@ def main(config): @main.command() @click.option("--placeholders", is_flag=True, default=False) @click.argument("files", nargs=-1, type=click.Path(exists=True), required=False) +@click.option("-t", "--test", required=False, default=False, is_flag=True) @config -def start(config, files, placeholders): +def start(config, files, placeholders, test): """ This is the entry point of starting DYC for the whole project. When you run `dyc start`. ParsedConfig will wrap all the @@ -44,7 +45,7 @@ def start(config, files, placeholders): """ if files: config.plain["file_list"] = list(files) - dyc = DYC(config.plain, placeholders=placeholders) + dyc = DYC(config.plain, placeholders=placeholders, test=test) dyc.prepare() dyc.process_methods() dyc.process_top() diff --git a/dyc/main.py b/dyc/main.py index 0cac4b1..4ba2957 100644 --- a/dyc/main.py +++ b/dyc/main.py @@ -14,9 +14,10 @@ class DYC(Processor): - def __init__(self, config, details=None, placeholders=False): + def __init__(self, config, details=None, placeholders=False, test=False): self.config = config self.placeholders = placeholders + self.test = test def process_methods(self, diff_only=False, changes=[]): """ @@ -46,7 +47,7 @@ def process_methods(self, diff_only=False, changes=[]): method_cnf = fmt.get("method", {}) method_cnf["arguments"] = fmt.get("arguments") builder = MethodBuilder( - filename, method_cnf, placeholders=self.placeholders + filename, method_cnf, placeholders=self.placeholders, test=self.test ) builder.initialize(change=change) builder.prompts() @@ -64,7 +65,7 @@ def process_classes(self): classes_cnf = fmt.get("class", {}) classes_cnf["parents"] = fmt.get("parents") builder = ClassBuilder( - filename, classes_cnf, placeholders=self.placeholders + filename, classes_cnf, placeholders=self.placeholders, test=self.test ) builder.initialize() builder.prompts() @@ -81,7 +82,7 @@ def process_top(self, diff_only=False): extension = get_extension(filename) fmt = self.formats.get(extension) top_cnf = fmt.get("top", {}) - builder = TopBuilder(filename, top_cnf, placeholders=self.placeholders) + builder = TopBuilder(filename, top_cnf, placeholders=self.placeholders, test=self.test) builder.prompts() builder.apply() builder.clear(filename) From 5833718afc3c3801d1e7535d83d6233dfcae5d3e Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 21:22:21 -0400 Subject: [PATCH 05/22] Add ClassBuilder test suite --- .../input_files_original/class_test_1.py | 14 +++ .../cal_tests/oracles/class_test_1_correct.py | 46 +++++++++ our_tests/cal_tests/outputs/class_test_1.py | 46 +++++++++ our_tests/cal_tests/run_test.sh | 18 ++++ our_tests/cal_tests/test_guide.txt | 3 + .../cal_tests/user_input/class_test_1.in | 16 ++++ .../cal_tests/ze_other_files/class_test_2.in | 9 ++ .../cal_tests/ze_other_files/class_test_2.py | 17 ++++ .../ze_other_files/class_test_2_correct.py | 45 +++++++++ .../cal_tests/ze_other_files/class_test_3.in | 9 ++ .../cal_tests/ze_other_files/class_test_3.py | 17 ++++ .../ze_other_files/class_test_3_correct.py | 45 +++++++++ our_tests/test1.py | 94 +++++++++++++++++++ our_tests/test_classes.py | 55 +++++++++++ our_tests/test_input.txt | 4 + 15 files changed, 438 insertions(+) create mode 100644 our_tests/cal_tests/input_files_original/class_test_1.py create mode 100644 our_tests/cal_tests/oracles/class_test_1_correct.py create mode 100644 our_tests/cal_tests/outputs/class_test_1.py create mode 100755 our_tests/cal_tests/run_test.sh create mode 100644 our_tests/cal_tests/test_guide.txt create mode 100644 our_tests/cal_tests/user_input/class_test_1.in create mode 100644 our_tests/cal_tests/ze_other_files/class_test_2.in create mode 100644 our_tests/cal_tests/ze_other_files/class_test_2.py create mode 100644 our_tests/cal_tests/ze_other_files/class_test_2_correct.py create mode 100644 our_tests/cal_tests/ze_other_files/class_test_3.in create mode 100644 our_tests/cal_tests/ze_other_files/class_test_3.py create mode 100644 our_tests/cal_tests/ze_other_files/class_test_3_correct.py create mode 100644 our_tests/test1.py create mode 100644 our_tests/test_classes.py create mode 100644 our_tests/test_input.txt diff --git a/our_tests/cal_tests/input_files_original/class_test_1.py b/our_tests/cal_tests/input_files_original/class_test_1.py new file mode 100644 index 0000000..bd9ffa1 --- /dev/null +++ b/our_tests/cal_tests/input_files_original/class_test_1.py @@ -0,0 +1,14 @@ +class myClass(): + x = 1 + + class myClass1(Parent1): + y = 1 + + class myClass2(Parent1): + z = 1 + + class myClass3(Parent1): + a = 1 + +class myClass4(Parent1, Parent1): + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/oracles/class_test_1_correct.py b/our_tests/cal_tests/oracles/class_test_1_correct.py new file mode 100644 index 0000000..5188de2 --- /dev/null +++ b/our_tests/cal_tests/oracles/class_test_1_correct.py @@ -0,0 +1,46 @@ +class myClass(): + """ + myClass is the giga chad + """ + x = 1 + + class myClass1(Parent1): + """ + Screw this class tho + + Inheritance + ----------- + Parent1: Parent1 does cool stuff + """ + y = 1 + + class myClass2(Parent1): + """ + This is myClass2, bruh + + Inheritance + ----------- + Parent1: Parent1 is alright + """ + z = 1 + + class myClass3(Parent1): + """ + This is myClass3, sir + + Inheritance + ----------- + Parent1: Parent1 is mighty fine + """ + a = 1 + +class myClass4(Parent1, Parent1): + """ + This is myClass4, MAGGOT + + Inheritance + ----------- + Parent1: Parent1 is chill + Parent1: Parent2 is the goat + """ + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/outputs/class_test_1.py b/our_tests/cal_tests/outputs/class_test_1.py new file mode 100644 index 0000000..5188de2 --- /dev/null +++ b/our_tests/cal_tests/outputs/class_test_1.py @@ -0,0 +1,46 @@ +class myClass(): + """ + myClass is the giga chad + """ + x = 1 + + class myClass1(Parent1): + """ + Screw this class tho + + Inheritance + ----------- + Parent1: Parent1 does cool stuff + """ + y = 1 + + class myClass2(Parent1): + """ + This is myClass2, bruh + + Inheritance + ----------- + Parent1: Parent1 is alright + """ + z = 1 + + class myClass3(Parent1): + """ + This is myClass3, sir + + Inheritance + ----------- + Parent1: Parent1 is mighty fine + """ + a = 1 + +class myClass4(Parent1, Parent1): + """ + This is myClass4, MAGGOT + + Inheritance + ----------- + Parent1: Parent1 is chill + Parent1: Parent2 is the goat + """ + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/run_test.sh b/our_tests/cal_tests/run_test.sh new file mode 100755 index 0000000..a0093f1 --- /dev/null +++ b/our_tests/cal_tests/run_test.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# IGNORE THIS +# Make ../input_files_original if it doesn't already exist +# [ -d "../input_files_original" ] || mkdir ../input_files_original + + +# RUN BY CALLING '../run_test.sh' WITHIN the input_files FOLDER +# Copy the files of the originals over since the current ones will be mutated by dyc +cp ../input_files_original/* . + +dyc start --test < ../user_input/class_test_1.in + +cp ./* ../outputs && rm ./* + +diff ../outputs/* ../oracles/* && \ +echo "========== TEST CASE PASS ==========" || \ +echo "\nX X X X X TEST CASE FAIL X X X X X" \ No newline at end of file diff --git a/our_tests/cal_tests/test_guide.txt b/our_tests/cal_tests/test_guide.txt new file mode 100644 index 0000000..518bac4 --- /dev/null +++ b/our_tests/cal_tests/test_guide.txt @@ -0,0 +1,3 @@ +Input test files +============================= +This \ No newline at end of file diff --git a/our_tests/cal_tests/user_input/class_test_1.in b/our_tests/cal_tests/user_input/class_test_1.in new file mode 100644 index 0000000..a701351 --- /dev/null +++ b/our_tests/cal_tests/user_input/class_test_1.in @@ -0,0 +1,16 @@ +N +y +y +y +y +y +myClass is the giga chad +Screw this class tho +Parent1 does cool stuff +This is myClass2, bruh +Parent1 is alright +This is myClass3, sir +Parent1 is mighty fine +This is myClass4, MAGGOT +Parent1 is chill +Parent2 is the goat diff --git a/our_tests/cal_tests/ze_other_files/class_test_2.in b/our_tests/cal_tests/ze_other_files/class_test_2.in new file mode 100644 index 0000000..96a6686 --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_2.in @@ -0,0 +1,9 @@ +y +y +y +y +This is myClass +This is myClass1 +This is myClass3 +This is myClass4 +:wq diff --git a/our_tests/cal_tests/ze_other_files/class_test_2.py b/our_tests/cal_tests/ze_other_files/class_test_2.py new file mode 100644 index 0000000..4464f38 --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_2.py @@ -0,0 +1,17 @@ +class myClass(): + x = 1 + + class myClass1(Parent1): + y = 1 + + class myClass2(Parent1): + """ + Test + """ + z = 1 + + class myClass3(Parent1): + a = 1 + +class myClass4(Parent1, Parent1): + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/ze_other_files/class_test_2_correct.py b/our_tests/cal_tests/ze_other_files/class_test_2_correct.py new file mode 100644 index 0000000..860bccf --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_2_correct.py @@ -0,0 +1,45 @@ +class myClass(): + """ + This is myClass + + Inheritance + ----------- + + """ + x = 1 + + class myClass1(Parent1): + """ + This is myClass1 + + Inheritance + ----------- + + """ + y = 1 + + class myClass2(Parent1): + """ + Test + """ + z = 1 + + class myClass3(Parent1): + """ + This is myClass3 + + Inheritance + ----------- + + """ + a = 1 + +class myClass4(Parent1, Parent1): + """ + This is myClass4 + + Inheritance + ----------- + + """ + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/ze_other_files/class_test_3.in b/our_tests/cal_tests/ze_other_files/class_test_3.in new file mode 100644 index 0000000..0005f12 --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_3.in @@ -0,0 +1,9 @@ +y +y +y +y +This is myClass +This is myClass1 +This is myClass2 +This is myClass4 +:wq diff --git a/our_tests/cal_tests/ze_other_files/class_test_3.py b/our_tests/cal_tests/ze_other_files/class_test_3.py new file mode 100644 index 0000000..b65d5ac --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_3.py @@ -0,0 +1,17 @@ +class myClass(): + x = 1 + + class myClass1(Parent1): + y = 1 + + class myClass2(Parent1): + z = 1 + + class myClass3(Parent1): + """ + Test + """ + a = 1 + +class myClass4(Parent1, Parent1): + b = 1 \ No newline at end of file diff --git a/our_tests/cal_tests/ze_other_files/class_test_3_correct.py b/our_tests/cal_tests/ze_other_files/class_test_3_correct.py new file mode 100644 index 0000000..2bc7720 --- /dev/null +++ b/our_tests/cal_tests/ze_other_files/class_test_3_correct.py @@ -0,0 +1,45 @@ +class myClass(): + """ + This is myClass + + Inheritance + ----------- + + """ + x = 1 + + class myClass1(Parent1): + """ + This is myClass1 + + Inheritance + ----------- + + """ + y = 1 + + class myClass2(Parent1): + """ + This is myClass2 + + Inheritance + ----------- + + """ + z = 1 + + class myClass3(Parent1): + """ + Test + """ + a = 1 + +class myClass4(Parent1, Parent1): + """ + This is myClass4 + + Inheritance + ----------- + + """ + b = 1 \ No newline at end of file diff --git a/our_tests/test1.py b/our_tests/test1.py new file mode 100644 index 0000000..73a35bd --- /dev/null +++ b/our_tests/test1.py @@ -0,0 +1,94 @@ +""" +bruh +""" +class myClass(x, y, +z): + """ + Has 3 parents + + Inheritance + ----------- + x: parent 1 + y: parente 2 + z: pare + """ + class nested_class: + """ + nested, no vars + """ + def __init__(self): + print('hi') + + + def initalized(self, x): + """ + this is a test + + Parameters + ---------- + int x: x is a variable of something + """ + print('def') + +class class2(x): + """ + op + + Inheritance + ----------- + x: free + """ + def ff(self): + """ + ff + + Parameters + ---------- + + """ + pass + + +class class3: + """ + bruh + + Inheritance + ----------- + + """ + + def __init__(self, arg_1): + self.bro = arg_1 + self.array = [] + + def myMemberFunction(self): + """ + member + + Parameters + ---------- + + """ + pass + + +def my_method(param): + """ + Method sets x to 2 + + Parameters + ---------- + None param: does nothing + """ + x = 2 + def method_two(x): + """ + literally nothing + + Parameters + ---------- + int x: not used + """ + pass + pass diff --git a/our_tests/test_classes.py b/our_tests/test_classes.py new file mode 100644 index 0000000..2035c25 --- /dev/null +++ b/our_tests/test_classes.py @@ -0,0 +1,55 @@ +from dyc.classes import ( + ClassBuilder, + ClassDetails, + ClassInterface, + ClassFormatter, +) + + +class TestClassInterface: + def testSetDocstring(self): + interface = ClassInterface('plain', 'name', 8, 11, 'empty_file', + ['x', 'y'], {}, 4, False, False) + interface.class_docstring = 'test' + assert interface.class_docstring == 'test' + +class TestClassDetails: + def testExtract(self): + detail = ClassDetails('(~, y, z)', {}) + detail.extract() + assert detail.parents == ['~', 'y', 'z'] + + def testSanitize(self): + detail = ClassDetails('(~invalid, y, z)', {}) + detail.extract() + assert detail.parents == ['~invalid','y','z'] + sanitized = list(detail.sanitize()) + assert sanitized == ['y', 'z'] + + detail_2 = ClassDetails('(valid, inval!d, also invalid)', {}) + detail_2.extract() + assert detail_2.parents == ['valid', 'inval!d', 'also invalid'] + sanitized = list(detail_2.sanitize()) + assert sanitized == ['valid'] + +class TestClassBuilder: + def testIsClass(self): + builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) + assert builder._is_class('class') == True + assert builder._is_class('classes = 5') == False + + def testGetName(self): + builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) + assert builder._get_name('class test') == 'test' + assert builder._get_name('class test()') == 'test' + assert builder._get_name('class test(\n):') == 'test' + + def testPrompts(self): + builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) + assert builder.details == dict() + assert builder.prompts() == None + + def testExtractClasses(self): + builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) + assert builder.extract_classes('class test(x,y,z)') == ['x', 'y', 'z'] + assert builder.extract_classes('class test:') == [] diff --git a/our_tests/test_input.txt b/our_tests/test_input.txt new file mode 100644 index 0000000..0abffdb --- /dev/null +++ b/our_tests/test_input.txt @@ -0,0 +1,4 @@ +hello +there +general +kenobi From 8bdf89e86703292c5676075a07debac949543f14 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 21:35:22 -0400 Subject: [PATCH 06/22] move test_classes.py to tests/ folder --- {our_tests => tests}/test_classes.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {our_tests => tests}/test_classes.py (100%) diff --git a/our_tests/test_classes.py b/tests/test_classes.py similarity index 100% rename from our_tests/test_classes.py rename to tests/test_classes.py From 4861eef5060671bc6f8d7438317b8405e207fb10 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 21:40:20 -0400 Subject: [PATCH 07/22] removed experimental test files --- our_tests/test1.py | 94 ---------------------------------------- our_tests/test_input.txt | 4 -- 2 files changed, 98 deletions(-) delete mode 100644 our_tests/test1.py delete mode 100644 our_tests/test_input.txt diff --git a/our_tests/test1.py b/our_tests/test1.py deleted file mode 100644 index 73a35bd..0000000 --- a/our_tests/test1.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -bruh -""" -class myClass(x, y, -z): - """ - Has 3 parents - - Inheritance - ----------- - x: parent 1 - y: parente 2 - z: pare - """ - class nested_class: - """ - nested, no vars - """ - def __init__(self): - print('hi') - - - def initalized(self, x): - """ - this is a test - - Parameters - ---------- - int x: x is a variable of something - """ - print('def') - -class class2(x): - """ - op - - Inheritance - ----------- - x: free - """ - def ff(self): - """ - ff - - Parameters - ---------- - - """ - pass - - -class class3: - """ - bruh - - Inheritance - ----------- - - """ - - def __init__(self, arg_1): - self.bro = arg_1 - self.array = [] - - def myMemberFunction(self): - """ - member - - Parameters - ---------- - - """ - pass - - -def my_method(param): - """ - Method sets x to 2 - - Parameters - ---------- - None param: does nothing - """ - x = 2 - def method_two(x): - """ - literally nothing - - Parameters - ---------- - int x: not used - """ - pass - pass diff --git a/our_tests/test_input.txt b/our_tests/test_input.txt deleted file mode 100644 index 0abffdb..0000000 --- a/our_tests/test_input.txt +++ /dev/null @@ -1,4 +0,0 @@ -hello -there -general -kenobi From 82a55e317e4a735b679f5438b9d6280d948c038b Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 21:40:20 -0400 Subject: [PATCH 08/22] remove experimental test files --- our_tests/test1.py | 94 ---------------------------------------- our_tests/test_input.txt | 4 -- 2 files changed, 98 deletions(-) delete mode 100644 our_tests/test1.py delete mode 100644 our_tests/test_input.txt diff --git a/our_tests/test1.py b/our_tests/test1.py deleted file mode 100644 index 73a35bd..0000000 --- a/our_tests/test1.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -bruh -""" -class myClass(x, y, -z): - """ - Has 3 parents - - Inheritance - ----------- - x: parent 1 - y: parente 2 - z: pare - """ - class nested_class: - """ - nested, no vars - """ - def __init__(self): - print('hi') - - - def initalized(self, x): - """ - this is a test - - Parameters - ---------- - int x: x is a variable of something - """ - print('def') - -class class2(x): - """ - op - - Inheritance - ----------- - x: free - """ - def ff(self): - """ - ff - - Parameters - ---------- - - """ - pass - - -class class3: - """ - bruh - - Inheritance - ----------- - - """ - - def __init__(self, arg_1): - self.bro = arg_1 - self.array = [] - - def myMemberFunction(self): - """ - member - - Parameters - ---------- - - """ - pass - - -def my_method(param): - """ - Method sets x to 2 - - Parameters - ---------- - None param: does nothing - """ - x = 2 - def method_two(x): - """ - literally nothing - - Parameters - ---------- - int x: not used - """ - pass - pass diff --git a/our_tests/test_input.txt b/our_tests/test_input.txt deleted file mode 100644 index 0abffdb..0000000 --- a/our_tests/test_input.txt +++ /dev/null @@ -1,4 +0,0 @@ -hello -there -general -kenobi From a1bf9592ab5f31d98e1068617ced2128ea69e658 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 22:10:12 -0400 Subject: [PATCH 09/22] Black style fix --- dyc/classes.py | 27 ++++++++++++++------------- dyc/main.py | 4 +++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/dyc/classes.py b/dyc/classes.py index dfafd81..a64f1e6 100644 --- a/dyc/classes.py +++ b/dyc/classes.py @@ -73,10 +73,6 @@ def is_first_line_documented(self, result): read_first_line = linecache.getline(result.filename, result.start) read_second_line = linecache.getline(result.filename, result.start + 1) finalTwoLines = read_first_line + "\n" + read_second_line - # 1. Changed long regex since the assumption is that the line already has the keyword (e.g. def, class) in it - # 2. Can use a simpler regex - # 3. Potentially replace """ with config[class][open] as value - # The open_brace_pattern is """ by default, but can be configured in the yml files open_brace_pattern = self.config.get("open", None) pattern = r":\n\s*?({})".format(open_brace_pattern) @@ -144,7 +140,7 @@ def extract_and_set_information(self, filename, start, line, length): config=self.config, leading_space=get_leading_whitespace(initial_line), placeholders=self.placeholders, - test=self.test + test=self.test, ) def extract_classes(self, line): @@ -167,7 +163,9 @@ def prompts(self): class_interface.prompt() if class_interface else None def _class_interface_gen(self): - # For each ClassInterface object in details[filename][class_name] + """ + A generator that yields the class interfaces + """ if not self.details: yield None for filename, func_pack in self.details.items(): @@ -385,7 +383,7 @@ def confirm(self, polished): try: result = "\n".join(class_split) - # If running an automated test + # If running an automated test, skip the editor confirmation if self.test: message = result else: @@ -393,7 +391,9 @@ def confirm(self, polished): "## CONFIRM: MODIFY DOCSTRING BETWEEN START AND END LINES ONLY\n\n" + result ) - message = result if message == None else "\n".join(message.split("\n")[2:]) + message = ( + result if message == None else "\n".join(message.split("\n")[2:]) + ) except: print("Quitting the program in the editor terminates the process. Thanks") sys.exit() @@ -520,8 +520,9 @@ def sanitize(self): """ Sanitizes classes to validate all classes are correct """ - # Updated filter function to remove invalid parent names - return list(filter( - lambda parent: not re.findall(r"[^a-zA-Z0-9_]", parent), - self.parents - )) + # Updated filter function to remove invalid parent names due to bad syntax + return list( + filter( + lambda parent: not re.findall(r"[^a-zA-Z0-9_]", parent), self.parents + ) + ) diff --git a/dyc/main.py b/dyc/main.py index 4ba2957..9f1130d 100644 --- a/dyc/main.py +++ b/dyc/main.py @@ -82,7 +82,9 @@ def process_top(self, diff_only=False): extension = get_extension(filename) fmt = self.formats.get(extension) top_cnf = fmt.get("top", {}) - builder = TopBuilder(filename, top_cnf, placeholders=self.placeholders, test=self.test) + builder = TopBuilder( + filename, top_cnf, placeholders=self.placeholders, test=self.test + ) builder.prompts() builder.apply() builder.clear(filename) From d1b76d75debfafef7a823516c5e1fc19590d1f9a Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 22:11:51 -0400 Subject: [PATCH 10/22] move automated class testing files and rename them --- .../input_files_original/class_test_1.py | 0 .../oracles/class_test_1_correct.py | 0 .../outputs/class_test_1.py | 0 automated_class_tests/run_test.sh | 53 +++++++++++++++++++ .../test_cases_in_progress}/class_test_2.in | 0 .../test_cases_in_progress}/class_test_2.py | 0 .../class_test_2_correct.py | 0 .../test_cases_in_progress}/class_test_3.in | 0 .../test_cases_in_progress}/class_test_3.py | 0 .../class_test_3_correct.py | 0 .../user_input/class_test_1.in | 0 our_tests/cal_tests/run_test.sh | 18 ------- our_tests/cal_tests/test_guide.txt | 3 -- 13 files changed, 53 insertions(+), 21 deletions(-) rename {our_tests/cal_tests => automated_class_tests}/input_files_original/class_test_1.py (100%) rename {our_tests/cal_tests => automated_class_tests}/oracles/class_test_1_correct.py (100%) rename {our_tests/cal_tests => automated_class_tests}/outputs/class_test_1.py (100%) create mode 100755 automated_class_tests/run_test.sh rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_2.in (100%) rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_2.py (100%) rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_2_correct.py (100%) rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_3.in (100%) rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_3.py (100%) rename {our_tests/cal_tests/ze_other_files => automated_class_tests/test_cases_in_progress}/class_test_3_correct.py (100%) rename {our_tests/cal_tests => automated_class_tests}/user_input/class_test_1.in (100%) delete mode 100755 our_tests/cal_tests/run_test.sh delete mode 100644 our_tests/cal_tests/test_guide.txt diff --git a/our_tests/cal_tests/input_files_original/class_test_1.py b/automated_class_tests/input_files_original/class_test_1.py similarity index 100% rename from our_tests/cal_tests/input_files_original/class_test_1.py rename to automated_class_tests/input_files_original/class_test_1.py diff --git a/our_tests/cal_tests/oracles/class_test_1_correct.py b/automated_class_tests/oracles/class_test_1_correct.py similarity index 100% rename from our_tests/cal_tests/oracles/class_test_1_correct.py rename to automated_class_tests/oracles/class_test_1_correct.py diff --git a/our_tests/cal_tests/outputs/class_test_1.py b/automated_class_tests/outputs/class_test_1.py similarity index 100% rename from our_tests/cal_tests/outputs/class_test_1.py rename to automated_class_tests/outputs/class_test_1.py diff --git a/automated_class_tests/run_test.sh b/automated_class_tests/run_test.sh new file mode 100755 index 0000000..c4cf3fd --- /dev/null +++ b/automated_class_tests/run_test.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +########### +# SUMMARY # +########### +# This script serves as the groundworks of an alternative automated testing scheme to Pytest +# for class docstrings made by DYC. +# This is useful for testing methods that require large amounts of user input and +# an oracle to test the output against. +# +# Further modificaiton is likely needed + +################ +# INSTRUCTIONS # +################ +# 0. This script require pre-creation of the following: +# - A test case python file. +# - Follows the naming convention of "class_test_.py". +# where is some integer. +# - Store the file in "input_files_original/". +# - A text file with user input pre-entered. +# - Follows the naming convention of "class_test_.in" +# where is some integer. +# - Store the file in "user_input/". +# - An oracle of the desired output for the test case to be +# compared against. +# - Follows the naming convention of "class_test__correct.py" +# where is some integer. +# - Store the file in "oracles/". +# 1. Ensure your test cases are in the 'input_files_original/' folder'. +# 2. Navigate to the 'input_files/' folder. +# 3. Run this script on a single file by calling '../run_test.sh ' +# Make sure you are within the 'input_files/' folder when you run the command! +# +################ +# SCRIPT START # +################ +# Copy the files of the originals to this directory to use them. +# Originals are the backups. The copies here will be modified. +cp ../input_files_original/class_test_$1.py . + +# Use the '--test' flag to bypass the text editor opened by Click +# in the DYC program as it's problematic for automated testing and +# is not needed anyway. +dyc start --test < ../user_input/class_test_$1.in + +# Move the modified files to the 'outputs' folder. +mv ./class_test_$1.py ../outputs + +# Compare the modified test case to the oracle +diff ../outputs/class_test_$1.py ../oracles/class_test_$1_correct.py && \ +( echo "" && \ +echo "========== TEST CASE PASS ==========" ) || \ +echo "\nX X X X X TEST CASE FAIL X X X X X" \ No newline at end of file diff --git a/our_tests/cal_tests/ze_other_files/class_test_2.in b/automated_class_tests/test_cases_in_progress/class_test_2.in similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_2.in rename to automated_class_tests/test_cases_in_progress/class_test_2.in diff --git a/our_tests/cal_tests/ze_other_files/class_test_2.py b/automated_class_tests/test_cases_in_progress/class_test_2.py similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_2.py rename to automated_class_tests/test_cases_in_progress/class_test_2.py diff --git a/our_tests/cal_tests/ze_other_files/class_test_2_correct.py b/automated_class_tests/test_cases_in_progress/class_test_2_correct.py similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_2_correct.py rename to automated_class_tests/test_cases_in_progress/class_test_2_correct.py diff --git a/our_tests/cal_tests/ze_other_files/class_test_3.in b/automated_class_tests/test_cases_in_progress/class_test_3.in similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_3.in rename to automated_class_tests/test_cases_in_progress/class_test_3.in diff --git a/our_tests/cal_tests/ze_other_files/class_test_3.py b/automated_class_tests/test_cases_in_progress/class_test_3.py similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_3.py rename to automated_class_tests/test_cases_in_progress/class_test_3.py diff --git a/our_tests/cal_tests/ze_other_files/class_test_3_correct.py b/automated_class_tests/test_cases_in_progress/class_test_3_correct.py similarity index 100% rename from our_tests/cal_tests/ze_other_files/class_test_3_correct.py rename to automated_class_tests/test_cases_in_progress/class_test_3_correct.py diff --git a/our_tests/cal_tests/user_input/class_test_1.in b/automated_class_tests/user_input/class_test_1.in similarity index 100% rename from our_tests/cal_tests/user_input/class_test_1.in rename to automated_class_tests/user_input/class_test_1.in diff --git a/our_tests/cal_tests/run_test.sh b/our_tests/cal_tests/run_test.sh deleted file mode 100755 index a0093f1..0000000 --- a/our_tests/cal_tests/run_test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -# IGNORE THIS -# Make ../input_files_original if it doesn't already exist -# [ -d "../input_files_original" ] || mkdir ../input_files_original - - -# RUN BY CALLING '../run_test.sh' WITHIN the input_files FOLDER -# Copy the files of the originals over since the current ones will be mutated by dyc -cp ../input_files_original/* . - -dyc start --test < ../user_input/class_test_1.in - -cp ./* ../outputs && rm ./* - -diff ../outputs/* ../oracles/* && \ -echo "========== TEST CASE PASS ==========" || \ -echo "\nX X X X X TEST CASE FAIL X X X X X" \ No newline at end of file diff --git a/our_tests/cal_tests/test_guide.txt b/our_tests/cal_tests/test_guide.txt deleted file mode 100644 index 518bac4..0000000 --- a/our_tests/cal_tests/test_guide.txt +++ /dev/null @@ -1,3 +0,0 @@ -Input test files -============================= -This \ No newline at end of file From 94b1faf05f9e6d7a96567a0a3165672d76489673 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 22:17:45 -0400 Subject: [PATCH 11/22] move around test files for consistency --- .../automated_class_tests}/input_files_original/class_test_1.py | 0 .../automated_class_tests}/oracles/class_test_1_correct.py | 0 .../automated_class_tests}/outputs/class_test_1.py | 0 .../automated_class_tests}/run_test.sh | 0 .../automated_class_tests}/test_cases_in_progress/class_test_2.in | 0 .../automated_class_tests}/test_cases_in_progress/class_test_2.py | 0 .../test_cases_in_progress/class_test_2_correct.py | 0 .../automated_class_tests}/test_cases_in_progress/class_test_3.in | 0 .../automated_class_tests}/test_cases_in_progress/class_test_3.py | 0 .../test_cases_in_progress/class_test_3_correct.py | 0 .../automated_class_tests}/user_input/class_test_1.in | 0 tests/{ => pytest_tests}/test_classes.py | 0 tests/{ => pytest_tests}/test_utils.py | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename {automated_class_tests => tests/automated_class_tests}/input_files_original/class_test_1.py (100%) rename {automated_class_tests => tests/automated_class_tests}/oracles/class_test_1_correct.py (100%) rename {automated_class_tests => tests/automated_class_tests}/outputs/class_test_1.py (100%) rename {automated_class_tests => tests/automated_class_tests}/run_test.sh (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_2.in (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_2.py (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_2_correct.py (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_3.in (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_3.py (100%) rename {automated_class_tests => tests/automated_class_tests}/test_cases_in_progress/class_test_3_correct.py (100%) rename {automated_class_tests => tests/automated_class_tests}/user_input/class_test_1.in (100%) rename tests/{ => pytest_tests}/test_classes.py (100%) rename tests/{ => pytest_tests}/test_utils.py (100%) diff --git a/automated_class_tests/input_files_original/class_test_1.py b/tests/automated_class_tests/input_files_original/class_test_1.py similarity index 100% rename from automated_class_tests/input_files_original/class_test_1.py rename to tests/automated_class_tests/input_files_original/class_test_1.py diff --git a/automated_class_tests/oracles/class_test_1_correct.py b/tests/automated_class_tests/oracles/class_test_1_correct.py similarity index 100% rename from automated_class_tests/oracles/class_test_1_correct.py rename to tests/automated_class_tests/oracles/class_test_1_correct.py diff --git a/automated_class_tests/outputs/class_test_1.py b/tests/automated_class_tests/outputs/class_test_1.py similarity index 100% rename from automated_class_tests/outputs/class_test_1.py rename to tests/automated_class_tests/outputs/class_test_1.py diff --git a/automated_class_tests/run_test.sh b/tests/automated_class_tests/run_test.sh similarity index 100% rename from automated_class_tests/run_test.sh rename to tests/automated_class_tests/run_test.sh diff --git a/automated_class_tests/test_cases_in_progress/class_test_2.in b/tests/automated_class_tests/test_cases_in_progress/class_test_2.in similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_2.in rename to tests/automated_class_tests/test_cases_in_progress/class_test_2.in diff --git a/automated_class_tests/test_cases_in_progress/class_test_2.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2.py similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_2.py rename to tests/automated_class_tests/test_cases_in_progress/class_test_2.py diff --git a/automated_class_tests/test_cases_in_progress/class_test_2_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_2_correct.py rename to tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py diff --git a/automated_class_tests/test_cases_in_progress/class_test_3.in b/tests/automated_class_tests/test_cases_in_progress/class_test_3.in similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_3.in rename to tests/automated_class_tests/test_cases_in_progress/class_test_3.in diff --git a/automated_class_tests/test_cases_in_progress/class_test_3.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3.py similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_3.py rename to tests/automated_class_tests/test_cases_in_progress/class_test_3.py diff --git a/automated_class_tests/test_cases_in_progress/class_test_3_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py similarity index 100% rename from automated_class_tests/test_cases_in_progress/class_test_3_correct.py rename to tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py diff --git a/automated_class_tests/user_input/class_test_1.in b/tests/automated_class_tests/user_input/class_test_1.in similarity index 100% rename from automated_class_tests/user_input/class_test_1.in rename to tests/automated_class_tests/user_input/class_test_1.in diff --git a/tests/test_classes.py b/tests/pytest_tests/test_classes.py similarity index 100% rename from tests/test_classes.py rename to tests/pytest_tests/test_classes.py diff --git a/tests/test_utils.py b/tests/pytest_tests/test_utils.py similarity index 100% rename from tests/test_utils.py rename to tests/pytest_tests/test_utils.py From 78ddb1751536baeef424cfb7af64a57e2efa6182 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 23:31:45 -0400 Subject: [PATCH 12/22] fix classes testing suite --- setup.py | 2 +- .../input_files_original/class_test_1.py | 18 ++++-- .../oracles/class_test_1_correct.py | 47 +++++++++------- .../outputs/class_test_1.py | 46 ---------------- tests/automated_class_tests/run_test.sh | 2 +- .../test_cases_in_progress/class_test_2.py | 6 +- .../class_test_2_correct.py | 16 ++++-- .../test_cases_in_progress/class_test_3.py | 6 +- .../class_test_3_correct.py | 16 ++++-- .../user_input/class_test_1.in | 21 +++---- tests/pytest_tests/test_classes.py | 55 ------------------- tests/pytest_tests/test_utils.py | 36 ++++++------ 12 files changed, 100 insertions(+), 171 deletions(-) delete mode 100644 tests/automated_class_tests/outputs/class_test_1.py delete mode 100644 tests/pytest_tests/test_classes.py diff --git a/setup.py b/setup.py index a44f63b..15824b3 100644 --- a/setup.py +++ b/setup.py @@ -21,5 +21,5 @@ "pre-commit==1.18.1", ], entry_points={"console_scripts": ["dyc=dyc.dyc:main"]}, - package_data={'': ['*.yaml']}, + package_data={"": ["*.yaml"]}, ) diff --git a/tests/automated_class_tests/input_files_original/class_test_1.py b/tests/automated_class_tests/input_files_original/class_test_1.py index bd9ffa1..f719a28 100644 --- a/tests/automated_class_tests/input_files_original/class_test_1.py +++ b/tests/automated_class_tests/input_files_original/class_test_1.py @@ -1,14 +1,20 @@ -class myClass(): +class MyClass: + x = 1 - class myClass1(Parent1): + class MyClass1(Parent1): + y = 1 - class myClass2(Parent1): + class MyClass2(Parent1, Parent2): + z = 1 - class myClass3(Parent1): + class MyClass3(Parent1): + a = 1 -class myClass4(Parent1, Parent1): - b = 1 \ No newline at end of file + +class MyClass4(Parent1, Parent2): + + b = 1 diff --git a/tests/automated_class_tests/oracles/class_test_1_correct.py b/tests/automated_class_tests/oracles/class_test_1_correct.py index 5188de2..f17a85f 100644 --- a/tests/automated_class_tests/oracles/class_test_1_correct.py +++ b/tests/automated_class_tests/oracles/class_test_1_correct.py @@ -1,46 +1,53 @@ -class myClass(): +class MyClass: """ - myClass is the giga chad + MyClass Description """ + x = 1 - class myClass1(Parent1): + class MyClass1(Parent1): """ - Screw this class tho - + MyClass1 Description + Inheritance ----------- - Parent1: Parent1 does cool stuff + Parent1: Parent1 Description """ + y = 1 - class myClass2(Parent1): + class MyClass2(Parent1, Parent2): """ - This is myClass2, bruh - + MyClass2 Description + Inheritance ----------- - Parent1: Parent1 is alright + Parent1: Parent1 Description + Parent2: Parent2 Description """ + z = 1 - class myClass3(Parent1): + class MyClass3(Parent1): """ - This is myClass3, sir - + MyClass3 Description + Inheritance ----------- - Parent1: Parent1 is mighty fine + Parent1: Parent1 Description """ + a = 1 -class myClass4(Parent1, Parent1): + +class MyClass4(Parent1, Parent2): """ - This is myClass4, MAGGOT - + MyClass4 Description + Inheritance ----------- - Parent1: Parent1 is chill - Parent1: Parent2 is the goat + Parent1: Parent1 Description + Parent2: Parent2 Description """ - b = 1 \ No newline at end of file + + b = 1 diff --git a/tests/automated_class_tests/outputs/class_test_1.py b/tests/automated_class_tests/outputs/class_test_1.py deleted file mode 100644 index 5188de2..0000000 --- a/tests/automated_class_tests/outputs/class_test_1.py +++ /dev/null @@ -1,46 +0,0 @@ -class myClass(): - """ - myClass is the giga chad - """ - x = 1 - - class myClass1(Parent1): - """ - Screw this class tho - - Inheritance - ----------- - Parent1: Parent1 does cool stuff - """ - y = 1 - - class myClass2(Parent1): - """ - This is myClass2, bruh - - Inheritance - ----------- - Parent1: Parent1 is alright - """ - z = 1 - - class myClass3(Parent1): - """ - This is myClass3, sir - - Inheritance - ----------- - Parent1: Parent1 is mighty fine - """ - a = 1 - -class myClass4(Parent1, Parent1): - """ - This is myClass4, MAGGOT - - Inheritance - ----------- - Parent1: Parent1 is chill - Parent1: Parent2 is the goat - """ - b = 1 \ No newline at end of file diff --git a/tests/automated_class_tests/run_test.sh b/tests/automated_class_tests/run_test.sh index c4cf3fd..f88527c 100755 --- a/tests/automated_class_tests/run_test.sh +++ b/tests/automated_class_tests/run_test.sh @@ -47,7 +47,7 @@ dyc start --test < ../user_input/class_test_$1.in mv ./class_test_$1.py ../outputs # Compare the modified test case to the oracle -diff ../outputs/class_test_$1.py ../oracles/class_test_$1_correct.py && \ +diff --ignore-trailing-space ../outputs/class_test_$1.py ../oracles/class_test_$1_correct.py && \ ( echo "" && \ echo "========== TEST CASE PASS ==========" ) || \ echo "\nX X X X X TEST CASE FAIL X X X X X" \ No newline at end of file diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_2.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2.py index 4464f38..02f41cd 100644 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_2.py +++ b/tests/automated_class_tests/test_cases_in_progress/class_test_2.py @@ -1,4 +1,4 @@ -class myClass(): +class myClass: x = 1 class myClass1(Parent1): @@ -8,10 +8,12 @@ class myClass2(Parent1): """ Test """ + z = 1 class myClass3(Parent1): a = 1 + class myClass4(Parent1, Parent1): - b = 1 \ No newline at end of file + b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py index 860bccf..a8094aa 100644 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py +++ b/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py @@ -1,4 +1,4 @@ -class myClass(): +class myClass: """ This is myClass @@ -6,6 +6,7 @@ class myClass(): ----------- """ + x = 1 class myClass1(Parent1): @@ -14,14 +15,16 @@ class myClass1(Parent1): Inheritance ----------- - + """ + y = 1 class myClass2(Parent1): """ Test """ + z = 1 class myClass3(Parent1): @@ -30,16 +33,19 @@ class myClass3(Parent1): Inheritance ----------- - + """ + a = 1 + class myClass4(Parent1, Parent1): """ This is myClass4 Inheritance ----------- - + """ - b = 1 \ No newline at end of file + + b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_3.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3.py index b65d5ac..ee3bafe 100644 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_3.py +++ b/tests/automated_class_tests/test_cases_in_progress/class_test_3.py @@ -1,4 +1,4 @@ -class myClass(): +class myClass: x = 1 class myClass1(Parent1): @@ -11,7 +11,9 @@ class myClass3(Parent1): """ Test """ + a = 1 + class myClass4(Parent1, Parent1): - b = 1 \ No newline at end of file + b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py index 2bc7720..5326b6e 100644 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py +++ b/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py @@ -1,4 +1,4 @@ -class myClass(): +class myClass: """ This is myClass @@ -6,6 +6,7 @@ class myClass(): ----------- """ + x = 1 class myClass1(Parent1): @@ -14,8 +15,9 @@ class myClass1(Parent1): Inheritance ----------- - + """ + y = 1 class myClass2(Parent1): @@ -24,22 +26,26 @@ class myClass2(Parent1): Inheritance ----------- - + """ + z = 1 class myClass3(Parent1): """ Test """ + a = 1 + class myClass4(Parent1, Parent1): """ This is myClass4 Inheritance ----------- - + """ - b = 1 \ No newline at end of file + + b = 1 diff --git a/tests/automated_class_tests/user_input/class_test_1.in b/tests/automated_class_tests/user_input/class_test_1.in index a701351..c543add 100644 --- a/tests/automated_class_tests/user_input/class_test_1.in +++ b/tests/automated_class_tests/user_input/class_test_1.in @@ -4,13 +4,14 @@ y y y y -myClass is the giga chad -Screw this class tho -Parent1 does cool stuff -This is myClass2, bruh -Parent1 is alright -This is myClass3, sir -Parent1 is mighty fine -This is myClass4, MAGGOT -Parent1 is chill -Parent2 is the goat +MyClass Description +MyClass1 Description +Parent1 Description +MyClass2 Description +Parent1 Description +Parent2 Description +MyClass3 Description +Parent1 Description +MyClass4 Description +Parent1 Description +Parent2 Description diff --git a/tests/pytest_tests/test_classes.py b/tests/pytest_tests/test_classes.py deleted file mode 100644 index 2035c25..0000000 --- a/tests/pytest_tests/test_classes.py +++ /dev/null @@ -1,55 +0,0 @@ -from dyc.classes import ( - ClassBuilder, - ClassDetails, - ClassInterface, - ClassFormatter, -) - - -class TestClassInterface: - def testSetDocstring(self): - interface = ClassInterface('plain', 'name', 8, 11, 'empty_file', - ['x', 'y'], {}, 4, False, False) - interface.class_docstring = 'test' - assert interface.class_docstring == 'test' - -class TestClassDetails: - def testExtract(self): - detail = ClassDetails('(~, y, z)', {}) - detail.extract() - assert detail.parents == ['~', 'y', 'z'] - - def testSanitize(self): - detail = ClassDetails('(~invalid, y, z)', {}) - detail.extract() - assert detail.parents == ['~invalid','y','z'] - sanitized = list(detail.sanitize()) - assert sanitized == ['y', 'z'] - - detail_2 = ClassDetails('(valid, inval!d, also invalid)', {}) - detail_2.extract() - assert detail_2.parents == ['valid', 'inval!d', 'also invalid'] - sanitized = list(detail_2.sanitize()) - assert sanitized == ['valid'] - -class TestClassBuilder: - def testIsClass(self): - builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) - assert builder._is_class('class') == True - assert builder._is_class('classes = 5') == False - - def testGetName(self): - builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) - assert builder._get_name('class test') == 'test' - assert builder._get_name('class test()') == 'test' - assert builder._get_name('class test(\n):') == 'test' - - def testPrompts(self): - builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) - assert builder.details == dict() - assert builder.prompts() == None - - def testExtractClasses(self): - builder = ClassBuilder('filename', {'keywords': ['class']}, placeholders=False, test=False) - assert builder.extract_classes('class test(x,y,z)') == ['x', 'y', 'z'] - assert builder.extract_classes('class test:') == [] diff --git a/tests/pytest_tests/test_utils.py b/tests/pytest_tests/test_utils.py index 026fb92..267ce6f 100644 --- a/tests/pytest_tests/test_utils.py +++ b/tests/pytest_tests/test_utils.py @@ -17,15 +17,15 @@ def test_tabs(self): """ """Test tabs functionality""" - text = '\t\tHello' - expected = '\t\t' + text = "\t\tHello" + expected = "\t\t" got = get_leading_whitespace(text) assert expected == got def test_whitespace(self): """Test whitespace functionality""" - space = ' ' - text = '{space}Such a long whitespace'.format(space=space) + space = " " + text = "{space}Such a long whitespace".format(space=space) expected = space got = get_leading_whitespace(text) assert expected == got @@ -40,7 +40,7 @@ def test_should_return_none_if_not_found(self): ---------- """ - random_path = '/path/to/non/existing/file.yaml' + random_path = "/path/to/non/existing/file.yaml" expected = None got = read_yaml(random_path) assert expected == got @@ -55,7 +55,7 @@ def test_tabs(self): ---------- """ - assert get_indent('tab') == '\t' + assert get_indent("tab") == "\t" def test_2_spaces(self): """ @@ -65,7 +65,7 @@ def test_2_spaces(self): ---------- """ - assert get_indent('2 spaces') == ' ' + assert get_indent("2 spaces") == " " def test_falsy_value(self): """ @@ -75,7 +75,7 @@ def test_falsy_value(self): ---------- """ - assert get_indent(False) == '' + assert get_indent(False) == "" def test_default_4_spaces(self): """ @@ -86,7 +86,7 @@ def test_default_4_spaces(self): ---------- """ - assert get_indent(None) == ' ' + assert get_indent(None) == " " class TestGetExtension: @@ -98,8 +98,8 @@ def test_existing_extension_valid(self): ---------- """ - ext = 'file.puk' - expected = 'puk' + ext = "file.puk" + expected = "puk" got = get_extension(ext) assert expected == got @@ -112,8 +112,8 @@ def test_non_existing_extension(self): ---------- """ - ext = 'file' - expected = '' + ext = "file" + expected = "" got = get_extension(ext) assert expected == got @@ -127,7 +127,7 @@ def test_wrong_extension_type(self): """ exts = [dict(), False, True, [], 123] - expected = '' + expected = "" for ext in exts: got = get_extension(ext) assert expected == got @@ -136,10 +136,10 @@ def test_wrong_extension_type(self): class TestIsComment: def test_valid_comments(self): """Testing valid comments""" - text = '# Hello World' - assert is_comment(text, ['#']) == True + text = "# Hello World" + assert is_comment(text, ["#"]) == True def test_invalid_comments(self): """Testing invalid comments""" - text = '# Hello World' - assert is_comment(text, ['//']) == False + text = "# Hello World" + assert is_comment(text, ["//"]) == False From d7aa09a0df6fd3713d5db971b8aec97af3c27592 Mon Sep 17 00:00:00 2001 From: guptash Date: Fri, 23 Apr 2021 23:37:42 -0400 Subject: [PATCH 13/22] Add message to test script --- tests/automated_class_tests/run_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/automated_class_tests/run_test.sh b/tests/automated_class_tests/run_test.sh index f88527c..f9e065e 100755 --- a/tests/automated_class_tests/run_test.sh +++ b/tests/automated_class_tests/run_test.sh @@ -27,7 +27,7 @@ # where is some integer. # - Store the file in "oracles/". # 1. Ensure your test cases are in the 'input_files_original/' folder'. -# 2. Navigate to the 'input_files/' folder. +# 2. Navigate to the 'input_files/' folder. Make the folder if needed. # 3. Run this script on a single file by calling '../run_test.sh ' # Make sure you are within the 'input_files/' folder when you run the command! # From 52bf7912c9163e8d252d8625bc0458e190771499 Mon Sep 17 00:00:00 2001 From: guptash Date: Sat, 24 Apr 2021 18:32:40 -0400 Subject: [PATCH 14/22] Change --test flag to --skip-confirm for clarity --- dyc/base.py | 4 ++-- dyc/classes.py | 14 +++++++------- dyc/dyc.py | 6 +++--- dyc/main.py | 19 ++++++++++++++----- tests/automated_class_tests/run_test.sh | 4 ++-- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/dyc/base.py b/dyc/base.py index 8d2acff..6ea064c 100644 --- a/dyc/base.py +++ b/dyc/base.py @@ -6,11 +6,11 @@ class Builder(object): - def __init__(self, filename, config, placeholders=False, test=False): + def __init__(self, filename, config, placeholders=False, skip_confirm=False): self.filename = filename self.config = config self.placeholders = placeholders - self.test = test + self.skip_confirm = skip_confirm details = dict() diff --git a/dyc/classes.py b/dyc/classes.py index a64f1e6..c0e90ef 100644 --- a/dyc/classes.py +++ b/dyc/classes.py @@ -140,7 +140,7 @@ def extract_and_set_information(self, filename, start, line, length): config=self.config, leading_space=get_leading_whitespace(initial_line), placeholders=self.placeholders, - test=self.test, + skip_confirm=self.skip_confirm, ) def extract_classes(self, line): @@ -242,7 +242,7 @@ class ClassFormatter: formatted_string = "{open}{break_after_open}{class_docstring}{break_after_docstring}{empty_line}{parents_format}{break_before_close}{close}" fmt = BlankFormatter() - def format(self, test): + def format(self, skip_confirm): """ Public formatting method that executes a pattern of methods to complete the process @@ -253,7 +253,7 @@ def format(self, test): self.result = self.fmt.format(self.formatted_string, **self.class_format) self.add_indentation() self.polish() - self.test = test + self.skip_confirm = skip_confirm def wrap_strings(self, words): """ @@ -384,7 +384,7 @@ def confirm(self, polished): result = "\n".join(class_split) # If running an automated test, skip the editor confirmation - if self.test: + if self.skip_confirm: message = result else: message = click.edit( @@ -437,7 +437,7 @@ def __init__( config, leading_space, placeholders, - test, + skip_confirm, ): self.plain = plain self.name = name @@ -450,12 +450,12 @@ def __init__( self.config = config self.leading_space = leading_space self.placeholders = placeholders - self.test = test + self.skip_confirm = skip_confirm def prompt(self): self._prompt_docstring() self._prompt_parents() - self.format(test=self.test) + self.format(skip_confirm=self.skip_confirm) def _prompt_docstring(self): """ diff --git a/dyc/dyc.py b/dyc/dyc.py index 42b8df4..b04fe49 100644 --- a/dyc/dyc.py +++ b/dyc/dyc.py @@ -34,9 +34,9 @@ def main(config): @main.command() @click.option("--placeholders", is_flag=True, default=False) @click.argument("files", nargs=-1, type=click.Path(exists=True), required=False) -@click.option("-t", "--test", required=False, default=False, is_flag=True) +@click.option("--skip-confirm", required=False, default=False, is_flag=True) @config -def start(config, files, placeholders, test): +def start(config, files, placeholders, skip_confirm): """ This is the entry point of starting DYC for the whole project. When you run `dyc start`. ParsedConfig will wrap all the @@ -45,7 +45,7 @@ def start(config, files, placeholders, test): """ if files: config.plain["file_list"] = list(files) - dyc = DYC(config.plain, placeholders=placeholders, test=test) + dyc = DYC(config.plain, placeholders=placeholders, skip_confirm=skip_confirm) dyc.prepare() dyc.process_methods() dyc.process_top() diff --git a/dyc/main.py b/dyc/main.py index 9f1130d..81ba604 100644 --- a/dyc/main.py +++ b/dyc/main.py @@ -14,10 +14,10 @@ class DYC(Processor): - def __init__(self, config, details=None, placeholders=False, test=False): + def __init__(self, config, details=None, placeholders=False, skip_confirm=False): self.config = config self.placeholders = placeholders - self.test = test + self.skip_confirm = skip_confirm def process_methods(self, diff_only=False, changes=[]): """ @@ -47,7 +47,10 @@ def process_methods(self, diff_only=False, changes=[]): method_cnf = fmt.get("method", {}) method_cnf["arguments"] = fmt.get("arguments") builder = MethodBuilder( - filename, method_cnf, placeholders=self.placeholders, test=self.test + filename, + method_cnf, + placeholders=self.placeholders, + skip_confirm=self.skip_confirm, ) builder.initialize(change=change) builder.prompts() @@ -65,7 +68,10 @@ def process_classes(self): classes_cnf = fmt.get("class", {}) classes_cnf["parents"] = fmt.get("parents") builder = ClassBuilder( - filename, classes_cnf, placeholders=self.placeholders, test=self.test + filename, + classes_cnf, + placeholders=self.placeholders, + skip_confirm=self.skip_confirm, ) builder.initialize() builder.prompts() @@ -83,7 +89,10 @@ def process_top(self, diff_only=False): fmt = self.formats.get(extension) top_cnf = fmt.get("top", {}) builder = TopBuilder( - filename, top_cnf, placeholders=self.placeholders, test=self.test + filename, + top_cnf, + placeholders=self.placeholders, + skip_confirm=self.skip_confirm, ) builder.prompts() builder.apply() diff --git a/tests/automated_class_tests/run_test.sh b/tests/automated_class_tests/run_test.sh index f9e065e..edbb4a3 100755 --- a/tests/automated_class_tests/run_test.sh +++ b/tests/automated_class_tests/run_test.sh @@ -38,10 +38,10 @@ # Originals are the backups. The copies here will be modified. cp ../input_files_original/class_test_$1.py . -# Use the '--test' flag to bypass the text editor opened by Click +# Use the '--skip-confirm' flag to bypass the text editor opened by Click # in the DYC program as it's problematic for automated testing and # is not needed anyway. -dyc start --test < ../user_input/class_test_$1.in +dyc start --skip-confirm < ../user_input/class_test_$1.in # Move the modified files to the 'outputs' folder. mv ./class_test_$1.py ../outputs From 3a271027fb926799361221b36884addd9451375b Mon Sep 17 00:00:00 2001 From: guptash Date: Sat, 24 Apr 2021 18:34:04 -0400 Subject: [PATCH 15/22] Remove unnecessary default options --- dyc/configs/defaults.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/dyc/configs/defaults.yaml b/dyc/configs/defaults.yaml index 19e1e0e..f99e8a5 100644 --- a/dyc/configs/defaults.yaml +++ b/dyc/configs/defaults.yaml @@ -51,6 +51,5 @@ formats: parents: title: 'Inheritance' underline: true - add_type: false inline: false prefix: '' From 56cf7fb1d3943e81339d057563cde14823deb90f Mon Sep 17 00:00:00 2001 From: guptash Date: Sat, 24 Apr 2021 18:34:48 -0400 Subject: [PATCH 16/22] Update README to include class options --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 531be3d..d26483f 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,16 @@ To run on a Git Diff patch. Run $ dyc diff ``` - To have Docstrings prepended on a method while development. Run the following command ```sh $ dyc diff --watch ``` +In order to bypass the text editor pop-up in the confirmation stage. Run +```sh +$ dyc start --skip-confirm +``` ## Method Docstring Options *You can also Setup your own customized Docstring Method Formatting in `dyc.yaml` within `formats` key* @@ -93,15 +96,47 @@ $ dyc diff --watch | `ignore` | Arguments to ignore. | list | | `prefix` | A prefix like "@param". | str | +
+ ## Classes Docstring Options -// TODO +*You can also Setup your own customized Docstring Class Formatting in `dyc.yaml` within `formats` key* + + +*Class* + +| Key | Description | Type | +|:-----------------------: |:-----------------------------------------------------------------------------------------------------------------------: |------| +| `keywords` | The necessary keyword to search for in a line that triggers actions (default = ["class"]) | list | +| `enabled` | Determine if formatting is enabled for the extension | bool | +| `indent` | Indentation in a class. Limited options ['tab', '2 spaces', '4 spaces'] | str | +| `indent_content` | Confirm if the content of a docstring has to be indented as well | bool | +| `open` | Starting opener text of a method | str | +| `close` | Close text of a class. This could be the same as opened, but not all languages opening and closing docstrings are same | str | +| `comments` | Comments symbols | str | +| `break_after_open` | Do we add a new line after adding the open strings of a class? | bool | +| `break_after_docstring` | Do we add a new line after adding the docstring? | bool | +| `break_before_close` | Add a new line before closing docstrings on a class | bool | +| `words_per_line` | How many words do we add per docstring? | int | +| `within_scope` | Should the docstring be within the scope of the class or out? Just like JS Method docstrings | bool | + + +*Parents* + +| Key | Description | Type | +|:-----------: |:---------------------------------------------: |:----: | +| `title` | A title for arguments. i.e: "Inheritance" | str | +| `underline` | Underline the title | bool | +| `inline` | Add docstrings all inline or break within. | bool | +| `prefix` | A prefix like "@parent". | str | + +
## Top of file Options // TODO -### Example +## Example ```sh $ cd myapp/ @@ -260,6 +295,8 @@ def hello(name): ## Development +### Setup + Thank you for taking the time to contribute into this project. It is simple but could be really helpful moving forward to all developers. To get started. @@ -273,7 +310,6 @@ To get started. $ pip install --editable . ``` - Before commiting: Install the pre-commit hooks From 7552ac7615d2758c2766c0c186998a6e68305fdf Mon Sep 17 00:00:00 2001 From: guptash Date: Sat, 24 Apr 2021 18:59:36 -0400 Subject: [PATCH 17/22] Update contributors list --- Contributors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Contributors.md b/Contributors.md index 4856d2c..b3fd344 100644 --- a/Contributors.md +++ b/Contributors.md @@ -9,3 +9,5 @@ * [RCristiano](https://github.com/RCristiano) * [Hildebrando Pedroni](https://github.com/HildePedroni) * [Saran Balaji](https://github.com/csaranbalaji) +* [Ashir Gupta](https://github.com/AshirGuptash) +* [Calvin Kerns](https://github.com/calkerns) From 1e9d474698431dc16c75a41e6147f772ee4a6814 Mon Sep 17 00:00:00 2001 From: guptash Date: Sun, 25 Apr 2021 13:13:54 -0400 Subject: [PATCH 18/22] change file directory level --- tests/test_classes.py | 75 ++++++++++++++++++++++++++ tests/{pytest_tests => }/test_utils.py | 0 2 files changed, 75 insertions(+) create mode 100644 tests/test_classes.py rename tests/{pytest_tests => }/test_utils.py (100%) diff --git a/tests/test_classes.py b/tests/test_classes.py new file mode 100644 index 0000000..5de1b58 --- /dev/null +++ b/tests/test_classes.py @@ -0,0 +1,75 @@ +from dyc.classes import ( + ClassBuilder, + ClassDetails, + ClassInterface, + ClassFormatter, +) +import click +from click.testing import CliRunner + + +class TestClassInterface: + def testSetDocstring(self): + @click.command() + def runner(): + interface = ClassInterface( + "plain", "name", 8, 11, "empty_file", ["x", "y"], {}, 4, False, False + ) + interface._prompt_docstring() + print(interface.class_docstring) + return + + runn = CliRunner() + result = runn.invoke(runner, input="test") + assert result.output.split("\n")[2] == "test" + + +class TestClassDetails: + def testExtract(self): + detail = ClassDetails("(~, y, z)", {}) + detail.extract() + assert detail.parents == ["~", "y", "z"] + + def testSanitize(self): + detail = ClassDetails("(~invalid, y, z)", {}) + detail.extract() + assert detail.parents == ["~invalid", "y", "z"] + sanitized = list(detail.sanitize()) + assert sanitized == ["y", "z"] + + detail_2 = ClassDetails("(valid, inval!d, also invalid)", {}) + detail_2.extract() + assert detail_2.parents == ["valid", "inval!d", "also invalid"] + sanitized = list(detail_2.sanitize()) + assert sanitized == ["valid"] + + +class TestClassBuilder: + def testIsClass(self): + builder = ClassBuilder( + "filename", {"keywords": ["class"]}, placeholders=False, skip_confirm=False + ) + assert builder._is_class("class") == True + assert builder._is_class("classes = 5") == False + + def testGetName(self): + builder = ClassBuilder( + "filename", {"keywords": ["class"]}, placeholders=False, skip_confirm=False + ) + assert builder._get_name("class test") == "test" + assert builder._get_name("class test()") == "test" + assert builder._get_name("class test(\n):") == "test" + + def testPrompts(self): + builder = ClassBuilder( + "filename", {"keywords": ["class"]}, placeholders=False, skip_confirm=False + ) + assert builder.details == dict() + assert builder.prompts() == None + + def testExtractClasses(self): + builder = ClassBuilder( + "filename", {"keywords": ["class"]}, placeholders=False, skip_confirm=False + ) + assert builder.extract_classes("class test(x,y,z)") == ["x", "y", "z"] + assert builder.extract_classes("class test:") == [] diff --git a/tests/pytest_tests/test_utils.py b/tests/test_utils.py similarity index 100% rename from tests/pytest_tests/test_utils.py rename to tests/test_utils.py From f9b4ba195f2f5ed071ba4ddfdba147ffd7800bef Mon Sep 17 00:00:00 2001 From: guptash Date: Sun, 25 Apr 2021 13:31:00 -0400 Subject: [PATCH 19/22] module fix --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From f59a562746242ba0a976f67efe04c2973e1bcbf0 Mon Sep 17 00:00:00 2001 From: guptash Date: Sun, 25 Apr 2021 20:33:55 -0400 Subject: [PATCH 20/22] Streamline test cases. Repurpose automated_class_tests folder into Pytest framework, delete old folder. --- dyc/dyc.py | 2 +- tests/automated_class_tests/run_test.sh | 53 ------------------- .../test_cases_in_progress/class_test_2.in | 9 ---- .../test_cases_in_progress/class_test_2.py | 19 ------- .../class_test_2_correct.py | 51 ------------------ .../test_cases_in_progress/class_test_3.in | 9 ---- .../test_cases_in_progress/class_test_3.py | 19 ------- .../class_test_3_correct.py | 51 ------------------ .../class_test_1.in | 0 .../class_test_1.py | 0 .../class_test_1_correct.py | 0 tests/test_classes.py | 32 +++++++++++ 12 files changed, 33 insertions(+), 212 deletions(-) delete mode 100755 tests/automated_class_tests/run_test.sh delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_2.in delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_2.py delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_3.in delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_3.py delete mode 100644 tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py rename tests/{automated_class_tests/user_input => test_case_files}/class_test_1.in (100%) rename tests/{automated_class_tests/input_files_original => test_case_files}/class_test_1.py (100%) mode change 100644 => 100755 rename tests/{automated_class_tests/oracles => test_case_files}/class_test_1_correct.py (100%) diff --git a/dyc/dyc.py b/dyc/dyc.py index b04fe49..1ff4d6a 100644 --- a/dyc/dyc.py +++ b/dyc/dyc.py @@ -24,7 +24,7 @@ def main(config): """ The entering method to the CLI - Parameterss + Parameters ---------- ConfigParser config: Config going to be used in DYC """ diff --git a/tests/automated_class_tests/run_test.sh b/tests/automated_class_tests/run_test.sh deleted file mode 100755 index edbb4a3..0000000 --- a/tests/automated_class_tests/run_test.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -########### -# SUMMARY # -########### -# This script serves as the groundworks of an alternative automated testing scheme to Pytest -# for class docstrings made by DYC. -# This is useful for testing methods that require large amounts of user input and -# an oracle to test the output against. -# -# Further modificaiton is likely needed - -################ -# INSTRUCTIONS # -################ -# 0. This script require pre-creation of the following: -# - A test case python file. -# - Follows the naming convention of "class_test_.py". -# where is some integer. -# - Store the file in "input_files_original/". -# - A text file with user input pre-entered. -# - Follows the naming convention of "class_test_.in" -# where is some integer. -# - Store the file in "user_input/". -# - An oracle of the desired output for the test case to be -# compared against. -# - Follows the naming convention of "class_test__correct.py" -# where is some integer. -# - Store the file in "oracles/". -# 1. Ensure your test cases are in the 'input_files_original/' folder'. -# 2. Navigate to the 'input_files/' folder. Make the folder if needed. -# 3. Run this script on a single file by calling '../run_test.sh ' -# Make sure you are within the 'input_files/' folder when you run the command! -# -################ -# SCRIPT START # -################ -# Copy the files of the originals to this directory to use them. -# Originals are the backups. The copies here will be modified. -cp ../input_files_original/class_test_$1.py . - -# Use the '--skip-confirm' flag to bypass the text editor opened by Click -# in the DYC program as it's problematic for automated testing and -# is not needed anyway. -dyc start --skip-confirm < ../user_input/class_test_$1.in - -# Move the modified files to the 'outputs' folder. -mv ./class_test_$1.py ../outputs - -# Compare the modified test case to the oracle -diff --ignore-trailing-space ../outputs/class_test_$1.py ../oracles/class_test_$1_correct.py && \ -( echo "" && \ -echo "========== TEST CASE PASS ==========" ) || \ -echo "\nX X X X X TEST CASE FAIL X X X X X" \ No newline at end of file diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_2.in b/tests/automated_class_tests/test_cases_in_progress/class_test_2.in deleted file mode 100644 index 96a6686..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_2.in +++ /dev/null @@ -1,9 +0,0 @@ -y -y -y -y -This is myClass -This is myClass1 -This is myClass3 -This is myClass4 -:wq diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_2.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2.py deleted file mode 100644 index 02f41cd..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_2.py +++ /dev/null @@ -1,19 +0,0 @@ -class myClass: - x = 1 - - class myClass1(Parent1): - y = 1 - - class myClass2(Parent1): - """ - Test - """ - - z = 1 - - class myClass3(Parent1): - a = 1 - - -class myClass4(Parent1, Parent1): - b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py deleted file mode 100644 index a8094aa..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_2_correct.py +++ /dev/null @@ -1,51 +0,0 @@ -class myClass: - """ - This is myClass - - Inheritance - ----------- - - """ - - x = 1 - - class myClass1(Parent1): - """ - This is myClass1 - - Inheritance - ----------- - - """ - - y = 1 - - class myClass2(Parent1): - """ - Test - """ - - z = 1 - - class myClass3(Parent1): - """ - This is myClass3 - - Inheritance - ----------- - - """ - - a = 1 - - -class myClass4(Parent1, Parent1): - """ - This is myClass4 - - Inheritance - ----------- - - """ - - b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_3.in b/tests/automated_class_tests/test_cases_in_progress/class_test_3.in deleted file mode 100644 index 0005f12..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_3.in +++ /dev/null @@ -1,9 +0,0 @@ -y -y -y -y -This is myClass -This is myClass1 -This is myClass2 -This is myClass4 -:wq diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_3.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3.py deleted file mode 100644 index ee3bafe..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_3.py +++ /dev/null @@ -1,19 +0,0 @@ -class myClass: - x = 1 - - class myClass1(Parent1): - y = 1 - - class myClass2(Parent1): - z = 1 - - class myClass3(Parent1): - """ - Test - """ - - a = 1 - - -class myClass4(Parent1, Parent1): - b = 1 diff --git a/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py b/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py deleted file mode 100644 index 5326b6e..0000000 --- a/tests/automated_class_tests/test_cases_in_progress/class_test_3_correct.py +++ /dev/null @@ -1,51 +0,0 @@ -class myClass: - """ - This is myClass - - Inheritance - ----------- - - """ - - x = 1 - - class myClass1(Parent1): - """ - This is myClass1 - - Inheritance - ----------- - - """ - - y = 1 - - class myClass2(Parent1): - """ - This is myClass2 - - Inheritance - ----------- - - """ - - z = 1 - - class myClass3(Parent1): - """ - Test - """ - - a = 1 - - -class myClass4(Parent1, Parent1): - """ - This is myClass4 - - Inheritance - ----------- - - """ - - b = 1 diff --git a/tests/automated_class_tests/user_input/class_test_1.in b/tests/test_case_files/class_test_1.in similarity index 100% rename from tests/automated_class_tests/user_input/class_test_1.in rename to tests/test_case_files/class_test_1.in diff --git a/tests/automated_class_tests/input_files_original/class_test_1.py b/tests/test_case_files/class_test_1.py old mode 100644 new mode 100755 similarity index 100% rename from tests/automated_class_tests/input_files_original/class_test_1.py rename to tests/test_case_files/class_test_1.py diff --git a/tests/automated_class_tests/oracles/class_test_1_correct.py b/tests/test_case_files/class_test_1_correct.py similarity index 100% rename from tests/automated_class_tests/oracles/class_test_1_correct.py rename to tests/test_case_files/class_test_1_correct.py diff --git a/tests/test_classes.py b/tests/test_classes.py index 5de1b58..414742f 100644 --- a/tests/test_classes.py +++ b/tests/test_classes.py @@ -4,8 +4,11 @@ ClassInterface, ClassFormatter, ) +from dyc.utils import get_extension +from dyc.dyc import start import click from click.testing import CliRunner +import pytest class TestClassInterface: @@ -20,6 +23,7 @@ def runner(): return runn = CliRunner() + # input = ["N", "y", "y", "Class 1 Descritposf"] result = runn.invoke(runner, input="test") assert result.output.split("\n")[2] == "test" @@ -73,3 +77,31 @@ def testExtractClasses(self): ) assert builder.extract_classes("class test(x,y,z)") == ["x", "y", "z"] assert builder.extract_classes("class test:") == [] + + @pytest.mark.parametrize("filename", ["tests/test_case_files/class_test_1.py"]) + def testProcess(self, filename): + # Get oracle file from the filename of the input file + oracle_file = filename.split(".")[0] + "_correct.py" + + # Save original contents of test file to a string to rewrite later + with open(filename, "r") as file_obj: + test_file_orig_contents = file_obj.read() + + # Save the user input + user_input_file = filename.split(".")[0] + ".in" + with open(user_input_file, "r") as user_input_obj: + user_input = user_input_obj.read() + + runner = CliRunner() + result = runner.invoke(start, ["--skip-confirm", filename], input=user_input) + + # Compare to Oracle + with open(filename, "r") as file_obj, open(oracle_file, "r") as oracle_obj: + # Replace dumb whitespace + assert file_obj.read().replace(" ", "") == oracle_obj.read().replace( + " ", "" + ) + + # Write back original contents + with open(filename, "w") as file_obj: + file_obj.write(test_file_orig_contents) From 0b13761f4ff97797a169c8ebbc2f44ba7c514520 Mon Sep 17 00:00:00 2001 From: guptash Date: Sun, 25 Apr 2021 20:56:29 -0400 Subject: [PATCH 21/22] Remove in progress test case, possibly revisit as part of issue #2 --- tests/test_classes.py | 57 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/tests/test_classes.py b/tests/test_classes.py index 414742f..54066b1 100644 --- a/tests/test_classes.py +++ b/tests/test_classes.py @@ -78,30 +78,33 @@ def testExtractClasses(self): assert builder.extract_classes("class test(x,y,z)") == ["x", "y", "z"] assert builder.extract_classes("class test:") == [] - @pytest.mark.parametrize("filename", ["tests/test_case_files/class_test_1.py"]) - def testProcess(self, filename): - # Get oracle file from the filename of the input file - oracle_file = filename.split(".")[0] + "_correct.py" - - # Save original contents of test file to a string to rewrite later - with open(filename, "r") as file_obj: - test_file_orig_contents = file_obj.read() - - # Save the user input - user_input_file = filename.split(".")[0] + ".in" - with open(user_input_file, "r") as user_input_obj: - user_input = user_input_obj.read() - - runner = CliRunner() - result = runner.invoke(start, ["--skip-confirm", filename], input=user_input) - - # Compare to Oracle - with open(filename, "r") as file_obj, open(oracle_file, "r") as oracle_obj: - # Replace dumb whitespace - assert file_obj.read().replace(" ", "") == oracle_obj.read().replace( - " ", "" - ) - - # Write back original contents - with open(filename, "w") as file_obj: - file_obj.write(test_file_orig_contents) + # IN PROGRESS BELOW + # CURRENT STATUS: PASSES ON PYTHON 3.8.5, BUT FAILS ON PYTHON 2.7.15 + + # @pytest.mark.parametrize("filename", ["tests/test_case_files/class_test_1.py"]) + # def testProcess(self, filename): + # # Get oracle file from the filename of the input file + # oracle_file = filename.split(".")[0] + "_correct.py" + + # # Save original contents of test file to a string to rewrite later + # with open(filename, "r") as file_obj: + # test_file_orig_contents = file_obj.read() + + # # Save the user input + # user_input_file = filename.split(".")[0] + ".in" + # with open(user_input_file, "r") as user_input_obj: + # user_input = user_input_obj.read() + + # runner = CliRunner() + # result = runner.invoke(start, ["--skip-confirm", filename], input=user_input) + + # # Compare to Oracle + # with open(filename, "r") as file_obj, open(oracle_file, "r") as oracle_obj: + # # Replace dumb whitespace + # assert file_obj.read().replace(" ", "") == oracle_obj.read().replace( + # " ", "" + # ) + + # # Write back original contents + # with open(filename, "w") as file_obj: + # file_obj.write(test_file_orig_contents) From ddc35e6c183137dc30b2a3a2f481098280167bd1 Mon Sep 17 00:00:00 2001 From: guptash Date: Sun, 25 Apr 2021 21:02:15 -0400 Subject: [PATCH 22/22] fix Black style --- dyc/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dyc/utils.py b/dyc/utils.py index 856708e..887e4f5 100644 --- a/dyc/utils.py +++ b/dyc/utils.py @@ -49,7 +49,7 @@ def __init__(self, default=""): self.default = default def get_value(self, key, args, kwds): - """""" + """ """ if isinstance(key, str): return kwds.get(key, self.default) else: