From e3685cd60c91bcb43a1d347e1abfa8bc163bc2ef Mon Sep 17 00:00:00 2001 From: Techno-Disaster Date: Wed, 22 Jan 2020 19:42:38 +0530 Subject: [PATCH 1/2] fix git ignore --- .gitignore | 5 +- TDebugger.egg-info/PKG-INFO | 3 +- build/lib/TDebugger/TDebugger.py | 109 ++++++++++++++++--------------- setup.py | 4 +- 4 files changed, 66 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 554980b..3dd1963 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ __pycache__ -result.json \ No newline at end of file +result.json +TDebugger.egg-info +dist +build \ No newline at end of file diff --git a/TDebugger.egg-info/PKG-INFO b/TDebugger.egg-info/PKG-INFO index 9810250..26f4cfb 100644 --- a/TDebugger.egg-info/PKG-INFO +++ b/TDebugger.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: TDebugger -Version: 0.1.0 +Version: 0.1.1 Summary: A advanced python debugger with live tracing that outputs video logs of a program's execution. Home-page: https://github.com/CCExtractor/TDebugger Author: Jayesh Nirve @@ -29,4 +29,5 @@ Description: # TDebugger Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 +Classifier: TDebugger Description-Content-Type: text/markdown diff --git a/build/lib/TDebugger/TDebugger.py b/build/lib/TDebugger/TDebugger.py index 41de0f7..f2ce885 100644 --- a/build/lib/TDebugger/TDebugger.py +++ b/build/lib/TDebugger/TDebugger.py @@ -333,56 +333,61 @@ def funcarg(argument): except ValueError: return argument -def main(): - parser = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter) - - debugGroup = parser.add_argument_group( - title="Analysis") - debugGroup.add_argument("--debug", "-d", metavar="FILE") - debugGroup.add_argument("--function", "-f", nargs='*', - ) - - debugGroup.add_argument("--output", "-o", metavar="FILE") - - printGroup = parser.add_argument_group( - title="Reporting") - printGroup.add_argument("--parse", "-p", metavar="FILE") - videoGroup = parser.add_argument_group( - title="Video Reporting", description="Generating a video displaying the program's flow and execution.") - videoGroup.add_argument("--video", "-v", - metavar=("PYTHON_FILE", "FUNCTION", "ANALYSIS_FILE", "VIDEO_OUTPUT"), nargs=4) - args = parser.parse_args() - - if args.debug: - debugpwd = args.debug - function_name = args.function[0] - function_args = list([funcarg(arg) for arg in args.function[1:]]) - - tdebugger = TDebugger(debugpwd, function_name, - function_args) - results = tdebugger.run() - - outputpwd = args.output - if outputpwd: - with open(outputpwd, "w") as f: - json.dump(results, f) - else: - terminal = Terminal(results) - terminal.terminal() - with open( - "./result.json", "wb") as f: - pickle.dump(results, f) - - elif args.parse: - parse_file_path = args.parse - with open(parse_file_path) as f: - data = json.load(f) - terminal = Terminal(data) - elif args.video: - with open(args.video[2], "rb") as f: - parsed_data = pickle.load(f) - reporter = VideoOutput(args.video[0], args.video[1], parsed_data) - reporter.generate_video(args.video[3]) + +parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter) + +debugGroup = parser.add_argument_group( + title="Analysis") +debugGroup.add_argument("--debug", "-d", help=".\n".join( + ["Path of a *.py file to debug", "Example: '--debug/-d main.py' will run the file main.py."]), metavar="FILE") +debugGroup.add_argument("--function", "-f", help=".\n".join( + ["If --debug FILE is present, optionally provide the name of a function to debug and function arguments", "(defaults to main with no arguments)", + "Example: '--func/-f foo 10' will run foo(10)."]), nargs='+', default=["main"], metavar=("FUNC", "PARAMETER")) + + +debugGroup.add_argument("--output", "-o", help="./n".join( + ["will output the logs in result.json file \nIMPORTANT name output file 'result.json' if you want to create a video later, \nExample: ' TDebugger -d foo.py -f test2 10 -o result.json'"]), metavar="FILE") + +printGroup = parser.add_argument_group( + title="Reporting") +printGroup.add_argument("--parse", "-p", help="./n".join( + ["parses a .json file(eg. the result.json file created with --output/-o argument) file in readable format."]), metavar="FILE") +videoGroup = parser.add_argument_group( + title="Video Reporting", description="Generating a video displaying the program's flow and execution.") +videoGroup.add_argument("--video", "-v", + metavar=("PYTHON_FILE", "FUNCTION", "ANALYSIS_FILE", "VIDEO_OUTPUT"), nargs=4) +args = parser.parse_args() + +if args.debug: + debugpwd = args.debug + function_name = args.function[0] + function_args = list([funcarg(arg) for arg in args.function[1:]]) + + tdebugger = TDebugger(debugpwd, function_name, + function_args) + results = tdebugger.run() + + outputpwd = args.output + if outputpwd: + with open(outputpwd, "w") as f: + json.dump(results, f) else: - print("Run <<\"python3 TDebugger.py --help\">>") + terminal = Terminal(results) + terminal.terminal() + with open( + "./result.json", "wb") as f: + pickle.dump(results, f) + +elif args.parse: + parse_file_path = args.parse + with open(parse_file_path) as f: + data = json.load(f) + terminal = Terminal(data) +elif args.video: + with open(args.video[2], "rb") as f: + parsed_data = pickle.load(f) + reporter = VideoOutput(args.video[0], args.video[1], parsed_data) + reporter.generate_video(args.video[3]) +else: + print("Run <<\"TDebugger --help\">>") diff --git a/setup.py b/setup.py index f571c6a..bcb6c8d 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ long_description = fh.read() setup(name="TDebugger", - version="0.1.0", + version="0.1.1", packages=["TDebugger", "TDebugger.TestAlgos"], entry_points={"console_scripts": [ "TDebugger = TDebugger.TDebugger:main"]}, @@ -14,10 +14,12 @@ long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/CCExtractor/TDebugger", + include_package_data=True, install_requires=['Pillow', 'opencv-python', 'numpy', 'pyyaml'], classifiers=[ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", + "TDebugger", ], ) From e724f6bca0c1f0e59f5c19101f2a23d0c98d4bf1 Mon Sep 17 00:00:00 2001 From: Techno-Disaster Date: Wed, 22 Jan 2020 19:43:54 +0530 Subject: [PATCH 2/2] remove unwanted files --- TDebugger.egg-info/PKG-INFO | 33 -- TDebugger.egg-info/SOURCES.txt | 11 - TDebugger.egg-info/dependency_links.txt | 1 - TDebugger.egg-info/entry_points.txt | 3 - TDebugger.egg-info/requires.txt | 4 - TDebugger.egg-info/top_level.txt | 1 - build/lib/TDebugger/TDebugger.py | 393 ----------------------- build/lib/TDebugger/TestAlgos/sorting.py | 105 ------ build/lib/TDebugger/TestAlgos/test.py | 17 - dist/TDebugger-0.1.0-py3-none-any.whl | Bin 7022 -> 0 bytes dist/TDebugger-0.1.0.tar.gz | Bin 6176 -> 0 bytes 11 files changed, 568 deletions(-) delete mode 100644 TDebugger.egg-info/PKG-INFO delete mode 100644 TDebugger.egg-info/SOURCES.txt delete mode 100644 TDebugger.egg-info/dependency_links.txt delete mode 100644 TDebugger.egg-info/entry_points.txt delete mode 100644 TDebugger.egg-info/requires.txt delete mode 100644 TDebugger.egg-info/top_level.txt delete mode 100644 build/lib/TDebugger/TDebugger.py delete mode 100644 build/lib/TDebugger/TestAlgos/sorting.py delete mode 100644 build/lib/TDebugger/TestAlgos/test.py delete mode 100644 dist/TDebugger-0.1.0-py3-none-any.whl delete mode 100644 dist/TDebugger-0.1.0.tar.gz diff --git a/TDebugger.egg-info/PKG-INFO b/TDebugger.egg-info/PKG-INFO deleted file mode 100644 index 26f4cfb..0000000 --- a/TDebugger.egg-info/PKG-INFO +++ /dev/null @@ -1,33 +0,0 @@ -Metadata-Version: 2.1 -Name: TDebugger -Version: 0.1.1 -Summary: A advanced python debugger with live tracing that outputs video logs of a program's execution. -Home-page: https://github.com/CCExtractor/TDebugger -Author: Jayesh Nirve -Author-email: nitinnirve@gmail.com -License: UNKNOWN -Description: # TDebugger - A advanced python debugger with live tracing - ## Installation - Use the `pip` package manager to install TDebugger - ```shell script - pip install TDebugger - ``` - Then, you can run it as a command-line tool: - ```shell script - TDebugger.py --help - ``` - - ## Output: - ![video](assets/tdebugger.gif) - ![output](https://github.com/Techno-Disaster/TDebugger/blob/master/assets/py6binarysearch.png) - - - - Moving to CCextracter. - -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: TDebugger -Description-Content-Type: text/markdown diff --git a/TDebugger.egg-info/SOURCES.txt b/TDebugger.egg-info/SOURCES.txt deleted file mode 100644 index 809f53d..0000000 --- a/TDebugger.egg-info/SOURCES.txt +++ /dev/null @@ -1,11 +0,0 @@ -README.md -setup.py -TDebugger/TDebugger.py -TDebugger.egg-info/PKG-INFO -TDebugger.egg-info/SOURCES.txt -TDebugger.egg-info/dependency_links.txt -TDebugger.egg-info/entry_points.txt -TDebugger.egg-info/requires.txt -TDebugger.egg-info/top_level.txt -TDebugger/TestAlgos/sorting.py -TDebugger/TestAlgos/test.py \ No newline at end of file diff --git a/TDebugger.egg-info/dependency_links.txt b/TDebugger.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/TDebugger.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/TDebugger.egg-info/entry_points.txt b/TDebugger.egg-info/entry_points.txt deleted file mode 100644 index 6591661..0000000 --- a/TDebugger.egg-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -TDebugger = TDebugger.TDebugger:main - diff --git a/TDebugger.egg-info/requires.txt b/TDebugger.egg-info/requires.txt deleted file mode 100644 index 08662c0..0000000 --- a/TDebugger.egg-info/requires.txt +++ /dev/null @@ -1,4 +0,0 @@ -Pillow -opencv-python -numpy -pyyaml diff --git a/TDebugger.egg-info/top_level.txt b/TDebugger.egg-info/top_level.txt deleted file mode 100644 index a366465..0000000 --- a/TDebugger.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -TDebugger diff --git a/build/lib/TDebugger/TDebugger.py b/build/lib/TDebugger/TDebugger.py deleted file mode 100644 index f2ce885..0000000 --- a/build/lib/TDebugger/TDebugger.py +++ /dev/null @@ -1,393 +0,0 @@ -import importlib.util -import sys -import copy -import time -import os -from datetime import datetime -import argparse -import json -import pickle -import inspect -from PIL import Image, ImageDraw, ImageFont -import cv2 as cv2 -import numpy - - -class TD: - def __init__(self, name, line, step, value): - self.name = name - self.line_value = [] - self.incrementor(line, step, value) - - def incrementor(self, line, step, value): - self.line_value.append({"line": line, "step": step, "value": value}) - - def getvariabletype(self): - value = self.line_value[0]["value"] - t = type(value) - for lv in self.line_value: - if type(lv["value"]) != t: - return "undefined" - return t - - def range(self): - if self.getvariabletype() in [int, float]: - values = [lv["value"] for lv in self.line_value] - return [min(values), max(values)] - - def dictionary(self): - return {"var": self.name, "type": str(self.getvariabletype()), "range": self.range(), "vallogs": self.line_value} - - -class Line: - def __init__(self, line_num): - self.line_num = line_num - self.times_executed = 0 - self.total_time = 0 - - def run_line(self, time): - self.times_executed += 1 - self.total_time += time - - def dictionary(self): - return {"line_num": self.line_num, "times_executed": self.times_executed, "total_time": self.total_time} - - -class TDebugger: - def __init__(self, file_path, function_name, function_args): - self.file_path = file_path - self.function_name = function_name - self.function_args = function_args - - self.curr_line = None - self.prev_variables = {} - self.variablelogs = {} - self.linelogs = {} - self.prev_time = time.time() - self.step = 0 - - self.results = {"code_info": {"filename": os.path.split(self.file_path)[1], "function_name": self.function_name, "function_args": self.function_args}, "logs": [], - "variablelogs": [], "linelogs": []} - - def __trace_calls(self, frame, event, arg): - self.curr_line = frame.f_lineno - if frame.f_code.co_name == self.function_name: - return self.__trace_lines - - def __trace_lines(self, frame, event, arg): - curr_logs = {"step": self.step, "timestamp": time.time( - ), "line_num": self.curr_line, "actions": []} - self.results["logs"].append(curr_logs) - - if self.curr_line not in self.linelogs: - self.linelogs[self.curr_line] = Line(self.curr_line) - self.linelogs[self.curr_line].run_line( - time.time() - self.prev_time) - curr_logs["line_runtime"] = self.linelogs[self.curr_line].dictionary( - ) - - self.first_print_for_this_line = True - current_variables = frame.f_locals - for var, val in current_variables.items(): - if var not in self.prev_variables: - curr_logs["actions"].append( - {"action": "init_var", "var": var, "val": val}) - self.variablelogs[var] = TD( - var, self.curr_line, self.step, copy.deepcopy(val)) - elif self.prev_variables[var] != val: - prev_val = self.prev_variables[var] - if isinstance(prev_val, list) and isinstance(val, list): - self.debuglist(var, prev_val, val) - elif isinstance(prev_val, dict) and isinstance(val, dict): - self.debugdict(var, prev_val, val) - else: - curr_logs["actions"].append( - {"action": "change_var", "var": var, "prev_val": prev_val, "new_val": val}) - self.variablelogs[var].incrementor( - self.curr_line, self.step, copy.deepcopy(val)) - - self.prev_variables = copy.deepcopy(current_variables) - self.prev_time = time.time() - self.curr_line = frame.f_lineno - self.step += 1 - - def debuglist(self, var, prev_val, val): - curr_logs = self.results["logs"][-1] - - for i in range(min(len(val), len(prev_val))): - if val[i] != prev_val[i]: - curr_logs["actions"].append( - {"action": "list_change", "var": var, "index": i, "prev_val": prev_val[i], "new_val": val[i]}) - if len(val) > len(prev_val): - for i in range(len(prev_val), len(val)): - curr_logs["actions"].append( - {"action": "list_add", "var": var, "index": i, "val": val[i]}) - if len(val) < len(prev_val): - for i in range(len(val), len(prev_val)): - curr_logs["actions"].append( - {"action": "list_remove", "var": var, "index": i}) - - def debugdict(self, var, prev_val, val): - curr_logs = self.results["logs"][-1] - - for elem in val: - if elem not in prev_val: - curr_logs["actions"].append( - {"action": "dict_add", "var": var, "key": elem, "val": val[elem]}) - elif prev_val[elem] != val[elem]: - curr_logs["actions"].append( - {"action": "dict_change", "var": var, "key": elem, "prev_val": prev_val[elem], "new_val": val[elem]}) - for elem in prev_val: - if elem not in val: - curr_logs["actions"].append( - {"action": "dict_remove", "var": var, "key": elem}) - - def run(self): - module_spec = importlib.util.spec_from_file_location( - "debugger", self.file_path) - module = importlib.util.module_from_spec(module_spec) - # print(module), - module_spec.loader.exec_module(module) - function = getattr(module, self.function_name) - - sys.settrace(self.__trace_calls) - self.prev_time = time.time() - function(*self.function_args) - sys.settrace(None) - - self.results["variablelogs"] = [var_obj.dictionary() - for var_obj in self.variablelogs.values()] - self.results["linelogs"] = [line_obj.dictionary() - for line_obj in self.linelogs.values()] - - return self.results - - -class VideoOutput: - def __init__(self, file_path, func_name, results): - module_spec = importlib.util.spec_from_file_location( - "sourcemodule", file_path) - module = importlib.util.module_from_spec(module_spec) - module_spec.loader.exec_module(module) - func = getattr(module, func_name) - - self.source_lines, self.start_line = inspect.getsourcelines(func) - self.results = results - - def framer(self, current_step, variablelogs, frame_size, font_size): - background = (0, 0, 0) - selectedline = (68, 71, 90) - normaltext = (255, 255, 255) - updatingtext = (57, 255, 20) - - img = Image.new("RGB", frame_size, color=background) - - draw = ImageDraw.Draw(img) - draw.rectangle((0, (current_step['line_num'] - self.start_line) * font_size, frame_size[0] * 0.4, (current_step['line_num'] - self.start_line + 1) * font_size), - fill=selectedline) - for line_offset, line in enumerate(self.source_lines): - draw.text((0, line_offset * font_size), - self.source_lines[line_offset], fill=normaltext) - - draw.text((0, frame_size[1] * 0.8), "Step: {}, line: {}".format( - current_step['step'], current_step['line_num']), fill=normaltext) - draw.text((0, frame_size[1] * 0.8 + font_size), - "Times executed: {}, time spent: {}".format( - current_step['line_runtime']['times_executed'], "{0:.2f}".format(current_step['line_runtime']['total_time'])), - fill=normaltext) - - current_text_y = 0 - variable_changes = {} - for action in current_step["actions"]: - action_desc = "Illegal action" - if action["action"] == "init_var": - action_desc = "created" - elif action["action"] == "change_var": - action_desc = "previous value {}".format(action["prev_val"]) - elif action["action"] == "list_add": - action_desc = "{}[{}] appended with value {}".format( - action["var"], action["index"], action["val"]) - elif action["action"] == "list_change": - action_desc = "{}[{}] changed from {} to {}".format( - action["var"], action["index"], action["prev_val"], action["new_val"]) - elif action["action"] == "list_remove": - action_desc = "{}[{}] removed".format( - action["var"], action["index"]) - elif action["action"] == "dict_add": - action_desc = "key {} added with value {}".format( - action["key"], action["val"]) - elif action["action"] == "dict_change": - action_desc = "value of key {} changed from {} to {}".format( - action["key"], action["prev_val"], action["new_val"]) - elif action["action"] == "dict_remove": - action_desc = "key {} removed".format(action["key"]) - - if action["var"] not in variable_changes: - variable_changes[action["var"]] = [] - variable_changes[action["var"]].append(action_desc) - - for variable in variablelogs: - curr_value = None - for val in variable['vallogs']: - if val['step'] > current_step['step']: - break - curr_value = val["value"] - - if variable['var'] in variable_changes: - message = "Variable {}, value {}, ".format( - variable['var'], curr_value) + ", ".join(variable_changes[variable['var']]) + "." - draw.text((frame_size[0] * 0.4 + 5, current_text_y), - message, fill=updatingtext) - elif curr_value is not None: - draw.text((frame_size[0] * 0.4 + 5, current_text_y), "Variable {}, value {}.".format( - variable['var'], curr_value), fill=normaltext) - current_text_y += font_size - - draw.line((frame_size[0] * 0.4, 0, frame_size[0] * - 0.4, frame_size[1]), fill=(255, 255, 255), width=5) - draw.line((0, frame_size[1] * 0.8, frame_size[0] * 0.4, - frame_size[1] * 0.8), fill=(255, 255, 255), width=5) - - return img - - def generate_video(self, output_path, frame_size=(2000, 1000), font_size=22, fps=1): - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - video = cv2.VideoWriter(output_path, fourcc, fps, frame_size) - - for step in self.results["logs"]: - img = self.framer( - step, self.results["variablelogs"], frame_size, font_size) - video.write(cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)) - - video.release() - - -class Terminal: - def __init__(self, results): - self.results = results - - def terminal(self): - - logs = self.results["logs"] - for step in logs: - print("{} - Step {}, line {} - executed {} times so far, total time so far {}s, average time so far {}s".format( - datetime.utcfromtimestamp(step["timestamp"]).strftime( - '%Y-%m-%d %H:%M:%S'), step["step"], step["line_num"], step["line_runtime"]["times_executed"], - "{0:0.07f}".format(step["line_runtime"]["total_time"]), "{0:0.07f}".format(step["line_runtime"]["total_time"] / step["line_runtime"]["times_executed"],),),) - - print("", end="") - if step["actions"]: - first = True - for action in step["actions"]: - if first: - first = False - else: - print(", ", end="") - - if action["action"] == "init_var": - action_desc = "variable '{}' created and initiated with {}".format( - action["var"], action["val"]) - elif action["action"] == "change_var": - action_desc = "variable '{}' changed from {} to {}".format( - action["var"], action["prev_val"], action["new_val"]) - elif action["action"] == "rm_var": - action_desc = "variable '{}' is deleted from memory {} to {}".format( - action["var"], action["prev_val"], action["None"]) - elif action["action"] == "list_add": - action_desc = "{}[{}] appended with value {}".format( - action["var"], action["index"], action["val"]) - elif action["action"] == "list_change": - action_desc = "{}[{}] changed from {} to {}".format( - action["var"], action["index"], action["prev_val"], action["new_val"]) - elif action["action"] == "list_remove": - action_desc = "{}[{}] removed".format( - action["var"], action["index"]) - elif action["action"] == "dict_add": - action_desc = "key {} added to {} with value {}".format( - action["key"], action["var"], action["val"]) - elif action["action"] == "dict_change": - action_desc = "value of key {} in {} changed from {} to {}".format( - action["key"], action["var"], action["prev_val"], action["new_val"]) - elif action["action"] == "dict_remove": - action_desc = "key {} removed from {}".format( - action["key"], action["var"]) - print(action_desc, end="") - print("") - print() - - linelogs = self.results["linelogs"] - print("", end="") - for line in linelogs: - print("Line {}: executed {} times, total runtime {}s".format(line["line_num"], line["times_executed"], "{0:0.07f}".format(line["total_time"]), - )) - print("", end="") - - -def funcarg(argument): - try: - return int(argument) - except ValueError: - try: - - return float(argument) - except ValueError: - return argument - - -parser = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter) - -debugGroup = parser.add_argument_group( - title="Analysis") -debugGroup.add_argument("--debug", "-d", help=".\n".join( - ["Path of a *.py file to debug", "Example: '--debug/-d main.py' will run the file main.py."]), metavar="FILE") -debugGroup.add_argument("--function", "-f", help=".\n".join( - ["If --debug FILE is present, optionally provide the name of a function to debug and function arguments", "(defaults to main with no arguments)", - "Example: '--func/-f foo 10' will run foo(10)."]), nargs='+', default=["main"], metavar=("FUNC", "PARAMETER")) - - -debugGroup.add_argument("--output", "-o", help="./n".join( - ["will output the logs in result.json file \nIMPORTANT name output file 'result.json' if you want to create a video later, \nExample: ' TDebugger -d foo.py -f test2 10 -o result.json'"]), metavar="FILE") - -printGroup = parser.add_argument_group( - title="Reporting") -printGroup.add_argument("--parse", "-p", help="./n".join( - ["parses a .json file(eg. the result.json file created with --output/-o argument) file in readable format."]), metavar="FILE") -videoGroup = parser.add_argument_group( - title="Video Reporting", description="Generating a video displaying the program's flow and execution.") -videoGroup.add_argument("--video", "-v", - metavar=("PYTHON_FILE", "FUNCTION", "ANALYSIS_FILE", "VIDEO_OUTPUT"), nargs=4) -args = parser.parse_args() - -if args.debug: - debugpwd = args.debug - function_name = args.function[0] - function_args = list([funcarg(arg) for arg in args.function[1:]]) - - tdebugger = TDebugger(debugpwd, function_name, - function_args) - results = tdebugger.run() - - outputpwd = args.output - if outputpwd: - with open(outputpwd, "w") as f: - json.dump(results, f) - else: - terminal = Terminal(results) - terminal.terminal() - with open( - "./result.json", "wb") as f: - pickle.dump(results, f) - -elif args.parse: - parse_file_path = args.parse - with open(parse_file_path) as f: - data = json.load(f) - terminal = Terminal(data) -elif args.video: - with open(args.video[2], "rb") as f: - parsed_data = pickle.load(f) - reporter = VideoOutput(args.video[0], args.video[1], parsed_data) - reporter.generate_video(args.video[3]) -else: - print("Run <<\"TDebugger --help\">>") diff --git a/build/lib/TDebugger/TestAlgos/sorting.py b/build/lib/TDebugger/TestAlgos/sorting.py deleted file mode 100644 index d9c4b64..0000000 --- a/build/lib/TDebugger/TestAlgos/sorting.py +++ /dev/null @@ -1,105 +0,0 @@ - - -def bubble_sort(lst=[19, 2, 31, 45, 30, 11, 121, 27]): - for iter_num in range(len(lst) - 1, 0, -1): - for idx in range(iter_num): - if lst[idx] > lst[idx + 1]: - temp = lst[idx] - lst[idx] = lst[idx + 1] - - lst[idx + 1] = temp - print(lst) - - -def merge_sort(lst=[64, 34, 25, 12, 22, 11, 90]): - - if len(lst) <= 1: - return lst - middle = len(lst) // 2 - left_list = lst[:middle] - right_list = lst[middle:] - - left_list = merge_sort(left_list) - right_list = merge_sort(right_list) - return list(merge(left_list, right_list)) - - -def merge(left_half, right_half): - res = [] - while len(left_half) != 0 and len(right_half) != 0: - if left_half[0] < right_half[0]: - res.append(left_half[0]) - left_half.remove(left_half[0]) - else: - res.append(right_half[0]) - right_half.remove(right_half[0]) - if len(left_half) == 0: - res = res + right_half - else: - res = res + left_half - return res - - -def insertion_sort(lst=[19, 2, 31, 45, 30, 11, 121, 27]): - for i in range(1, len(lst)): - j = i - 1 - nxt_element = lst[i] - while (lst[j] > nxt_element) and (j >= 0): - lst[j + 1] = lst[j] - j = j - 1 - lst[j + 1] = nxt_element - - -def shell_sort(lst=[19, 2, 31, 45, 30, 11, 121, 27]): - gap = len(lst) // 2 - while gap > 0: - for i in range(gap, len(lst)): - temp = lst[i] - j = i - while j >= gap and lst[j - gap] > temp: - lst[j] = lst[j - gap] - j = j - gap - lst[j] = temp - gap = gap // 2 - - -def selection_sort(lst): - for idx in range(len(lst)): - min_idx = idx - for j in range(idx + 1, len(lst)): - if lst[min_idx] > lst[j]: - min_idx = j - lst[idx], lst[min_idx] = lst[min_idx], lst[idx] -# From https://www.tutorialspoint.com/python_data_structure/python_sorting_algorithms.htm - - -def binary_search(arr=[2, 3, 4, 10, 40], l=0, r=4, x=10): - while l <= r: - mid = l + (r - l) // 2 - if arr[mid] == x: - return mid - elif arr[mid] < x: - l = mid + 1 - else: - r = mid - 1 - return -1 -# From https://www.geeksforgeeks.org/binary-search/ - - -def knapsack(W=50, wt=[10, 20, 30], val=[60, 100, 120], n=3): - if n == 0 or W == 0: - return 0 - - if wt[n - 1] > W: - return knapsack(W, wt, val, n - 1) - else: - return max(val[n - 1] + knapsack(W - wt[n - 1], wt, val, n - 1), knapsack(W, wt, val, n - 1)) -# From https://www.geeksforgeeks.org/python-program-for-dynamic-programming-set-10-0-1-knapsack-problem/ - - -# print(data) -# merge_sort(data), insertion_sort( -# data), selection_sort(data), - -# knapsack(50, [10, 20, 30], [60, 100, 120], 3), -# binary_search([2, 3, 4, 10, 40], 0, 4, 10), diff --git a/build/lib/TDebugger/TestAlgos/test.py b/build/lib/TDebugger/TestAlgos/test.py deleted file mode 100644 index 1537e61..0000000 --- a/build/lib/TDebugger/TestAlgos/test.py +++ /dev/null @@ -1,17 +0,0 @@ -def test1(start, end): - - for num in range(start, end + 1): - - if num % 2 == 0: - print(num, end=" ") - print("\n") # even numbers - - -def test2(n): - for i in range(2, n): - for j in range(2, int(i**.5)+1): - if i % j == 0: - break - else: - print(i) -# prime numbers diff --git a/dist/TDebugger-0.1.0-py3-none-any.whl b/dist/TDebugger-0.1.0-py3-none-any.whl deleted file mode 100644 index e8e75fbd6df017ce7f447e3f593a1949faf52b95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7022 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 diff --git a/dist/TDebugger-0.1.0.tar.gz b/dist/TDebugger-0.1.0.tar.gz deleted file mode 100644 index 6eabd85621e8dfeea38f6e59898aa0cbc5669794..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6176 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