From 8e3562c042c4a7ee80ad55c9f1386e17b8111246 Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Thu, 10 Nov 2022 18:24:27 +0800 Subject: [PATCH 01/11] add additional capabilities able to decorate class, able to decorate closures, enhanced exception handling --- reloading/reloading.py | 188 ++++++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 40 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index aef1353..3966b01 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -14,17 +14,19 @@ # hence we overwrite the iter to make sure that the error makes sense. class no_iter_partial(partial): def __iter__(self): - raise TypeError("Nothing to iterate over. Please pass an iterable to reloading.") + raise TypeError( + "Nothing to iterate over. Please pass an iterable to reloading." + ) def reloading(fn_or_seq=None, every=1, forever=None): - """Wraps a loop iterator or decorates a function to reload the source code + """Wraps a loop iterator or decorates a function to reload the source code before every loop iteration or function invocation. - When wrapped around the outermost iterator in a `for` loop, e.g. - `for i in reloading(range(10))`, causes the loop body to reload from source + When wrapped around the outermost iterator in a `for` loop, e.g. + `for i in reloading(range(10))`, causes the loop body to reload from source before every iteration while keeping the state. - When used as a function decorator, the decorated function is reloaded from + When used as a function decorator, the decorated function is reloaded from source before each execution. Pass the integer keyword argument `every` to reload the source code @@ -40,8 +42,14 @@ def reloading(fn_or_seq=None, every=1, forever=None): """ if fn_or_seq: - if isinstance(fn_or_seq, types.FunctionType): + fntypes = [types.FunctionType] + # type(someclass) -> + # print('TYPE:',type(fn_or_seq)) + # breakpoint() + if any(isinstance(fn_or_seq, fntype) for fntype in fntypes): return _reloading_function(fn_or_seq, every=every) + elif type(fn_or_seq) == type: + return _reloading_class(fn_or_seq, every=every) return _reloading_loop(fn_or_seq, every=every) if forever: return _reloading_loop(iter(int, 1), every=every) @@ -61,7 +69,7 @@ def format_itervars(ast_node): """Formats an `ast_node` of loop iteration variables as string, e.g. 'a, b'""" # handle the case that there only is a single loop var - if isinstance(ast_node, ast.Name): + if isinstance(ast_node, ast.Name): return ast_node.id names = [] @@ -70,7 +78,7 @@ def format_itervars(ast_node): names.append(child.id) elif isinstance(child, ast.Tuple) or isinstance(child, ast.List): # if its another tuple, like "a, (b, c)", recurse - names.append("({})".format(format_itervars(child))) + names.append("({})".format(format_itervars(child))) return ", ".join(names) @@ -78,7 +86,7 @@ def format_itervars(ast_node): def load_file(path): src = "" # while loop here since while saving, the file may sometimes be empty. - while (src == ""): + while src == "": with open(path, "r") as f: src = f.read() return src + "\n" @@ -101,14 +109,14 @@ def isolate_loop_body_and_get_itervars(tree, lineno, loop_id): candidate_nodes = [] for node in ast.walk(tree): if ( - isinstance(node, ast.For) + isinstance(node, ast.For) and isinstance(node.iter, ast.Call) - and node.iter.func.id == "reloading" + and node.iter.func.id == "reloading" and ( - (loop_id is not None and loop_id == get_loop_id(node)) - or getattr(node, "lineno", None) == lineno - ) - ): + (loop_id is not None and loop_id == get_loop_id(node)) + or getattr(node, "lineno", None) == lineno + ) + ): candidate_nodes.append(node) if len(candidate_nodes) > 1: @@ -120,15 +128,14 @@ def isolate_loop_body_and_get_itervars(tree, lineno, loop_id): raise LookupError( "Could not locate reloading loop. Please make sure the code in the line that uses `reloading` doesn't change between reloads." ) - + loop_node = candidate_nodes[0] tree.body = loop_node.body return loop_node.target, get_loop_id(loop_node) def get_loop_id(ast_node): - """Generates a unique identifier for an `ast_node` of type ast.For to find the loop in the changed source file - """ + """Generates a unique identifier for an `ast_node` of type ast.For to find the loop in the changed source file""" return ast.dump(ast_node.target) + "__" + ast.dump(ast_node.iter) @@ -137,8 +144,14 @@ def get_loop_code(loop_frame_info, loop_id): while True: tree = parse_file_until_successful(fpath) try: - itervars, found_loop_id = isolate_loop_body_and_get_itervars(tree, lineno=loop_frame_info[2], loop_id=loop_id) - return compile(tree, filename="", mode="exec"), format_itervars(itervars), found_loop_id + itervars, found_loop_id = isolate_loop_body_and_get_itervars( + tree, lineno=loop_frame_info[2], loop_id=loop_id + ) + return ( + compile(tree, filename="", mode="exec"), + format_itervars(itervars), + found_loop_id, + ) except LookupError: handle_exception(fpath) @@ -147,8 +160,16 @@ def handle_exception(fpath): exc = traceback.format_exc() exc = exc.replace('File ""', 'File "{}"'.format(fpath)) sys.stderr.write(exc + "\n") - print("Edit {} and press return to continue".format(fpath)) - sys.stdin.readline() + print("Edit {} and press return to continue, 'c' for continue, 'e' for exception.".format(fpath)) + # sys.stdin.readline() + signal = input() + signal_lower = signal.lower() + if signal_lower == 'c': + return True + elif signal_lower == 'e': + raise Exception('Raise exception for file: "{}"'.format(fpath)) + else: + return False def _reloading_loop(seq, every=1): @@ -158,14 +179,16 @@ def _reloading_loop(seq, every=1): caller_globals = loop_frame_info[0].f_globals caller_locals = loop_frame_info[0].f_locals - # create a unique name in the caller namespace that we can safely write + # create a unique name in the caller namespace that we can safely write # the values of the iteration variables into unique = unique_name(chain(caller_locals.keys(), caller_globals.keys())) loop_id = None for i, itervar_values in enumerate(seq): if i % every == 0: - compiled_body, itervars, loop_id = get_loop_code(loop_frame_info, loop_id=loop_id) + compiled_body, itervars, loop_id = get_loop_code( + loop_frame_info, loop_id=loop_id + ) caller_locals[unique] = itervar_values exec(itervars + " = " + unique, caller_globals, caller_locals) @@ -191,35 +214,44 @@ def strip_reloading_decorator(func): ] -def isolate_function_def(funcname, tree): +def isolate_function_def( + funcname, tree, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef] +): """Strip everything but the function definition from the ast in-place. Also strips the reloading decorator from the function definition""" for node in ast.walk(tree): if ( - isinstance(node, ast.FunctionDef) + any(isinstance(node, funcdef) for funcdef in funcdefs) and node.name == funcname - and "reloading" in [ - get_decorator_name(dec) - for dec in node.decorator_list - ] + and "reloading" in [get_decorator_name(dec) for dec in node.decorator_list] ): strip_reloading_decorator(node) - tree.body = [ node ] + tree.body = [node] return True return False -def get_function_def_code(fpath, fn): +def get_function_def_code(fpath, fn, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef]): tree = parse_file_until_successful(fpath) - found = isolate_function_def(fn.__name__, tree) + found = isolate_function_def(fn.__name__, tree, funcdefs=funcdefs) if not found: return None - compiled = compile(tree, filename="", mode="exec") + # print('tree fetched:',tree) # ast.Module object. + compiled = compile( + tree, filename=fpath, mode="exec" + ) # filename is the same as the original name? + # compiled = compile(tree, filename="", mode="exec") # filename is the same as the original name? return compiled -def get_reloaded_function(caller_globals, caller_locals, fpath, fn): - code = get_function_def_code(fpath, fn) +def get_reloaded_function( + caller_globals, + caller_locals, + fpath, + fn, + funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef], +): + code = get_function_def_code(fpath, fn, funcdefs=funcdefs) if code is None: return None # need to copy locals, otherwise the exec will overwrite the decorated with the undecorated new version @@ -230,11 +262,74 @@ def get_reloaded_function(caller_globals, caller_locals, fpath, fn): return func +def _reloading_class(fn, every=1): + stack = inspect.stack() + # print("stack", stack) + # breakpoint() + frame, fpath = stack[2][:2] + caller_locals = frame.f_locals + caller_globals = frame.f_globals + state = { + "class": None, + "reloads": -1, + } + # return mclass + + def wrapped(): + state["reloads"] += 1 + while True: + try: + if state["reloads"] % every == 0: + state["class"] = ( + get_reloaded_function( + caller_globals, + caller_locals, + fpath, + fn, + funcdefs=[ast.ClassDef], + ) + or state["class"] + ) + class_ = state["class"] + # the function inside function (closure) is not handled properly. need to decorate again? + # do not decorate already decorated function? + return class_ + except Exception: + while True: + needbreak = handle_exception(fpath) + if needbreak: break + try: + state["class"] = ( + get_reloaded_function( + caller_globals, + caller_locals, + fpath, + fn, + funcdefs=[ast.ClassDef], + ) + or state["class"] + ) + return state['class'] + except: + pass + class_ = wrapped() + caller_locals[fn.__name__] = class_ + return class_ + + def _reloading_function(fn, every=1): stack = inspect.stack() + # what is this stack? + # print(stack) + # breakpoint() + # stack[0] -> this _reloading_function + # stack[1] -> reloading function + # stack[2] -> original function + # FrameInfo(frame=>, filename='/media/root/parrot/pyjom/tests/skipexception_code_and_continue_resurrection_time_travel_debugging/hook_error_handler_to_see_if_context_preserved.py', lineno=67, function='', code_context=['def anotherFunction():\n'], index=0) frame, fpath = stack[2][:2] caller_locals = frame.f_locals caller_globals = frame.f_globals + # 'clear', 'f_back', 'f_builtins', 'f_code', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_trace', 'f_trace_lines', 'f_trace_opcodes' # crutch to use dict as python2 doesn't support nonlocal state = { @@ -244,15 +339,28 @@ def _reloading_function(fn, every=1): def wrapped(*args, **kwargs): if state["reloads"] % every == 0: - state["func"] = get_reloaded_function(caller_globals, caller_locals, fpath, fn) or state["func"] + state["func"] = ( + get_reloaded_function(caller_globals, caller_locals, fpath, fn) + or state["func"] + ) state["reloads"] += 1 while True: try: - result = state["func"](*args, **kwargs) + func = state["func"] + print( + f"----\nargs:{args}\nkwargs:{kwargs}\nfunc:{func}\n----" + ) # try to debug. + # the function inside function (closure) is not handled properly. need to decorate again? + # do not decorate already decorated function? + result = func(*args, **kwargs) return result except Exception: - handle_exception(fpath) - state["func"] = get_reloaded_function(caller_globals, caller_locals, fpath, fn) or state["func"] + needbreak = handle_exception(fpath) + if needbreak: break + state["func"] = ( + get_reloaded_function(caller_globals, caller_locals, fpath, fn) + or state["func"] + ) caller_locals[fn.__name__] = wrapped return wrapped From 5073a0ed18ecaa9818ec2cdfa7bdb6727647a703 Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Thu, 10 Nov 2022 18:50:57 +0800 Subject: [PATCH 02/11] enable stepping out to solve deeper problems adding prefix to filepath in `compile` as a hint of recursion depth --- reloading/reloading.py | 54 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 3966b01..4a14164 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -139,16 +139,17 @@ def get_loop_id(ast_node): return ast.dump(ast_node.target) + "__" + ast.dump(ast_node.iter) -def get_loop_code(loop_frame_info, loop_id): +def get_loop_code(loop_frame_info, loop_id, prefix="_RELOADING_"): fpath = loop_frame_info[1] + mfpath = removePrefix(fpath, prefix=prefix) while True: - tree = parse_file_until_successful(fpath) + tree = parse_file_until_successful(mfpath) try: itervars, found_loop_id = isolate_loop_body_and_get_itervars( tree, lineno=loop_frame_info[2], loop_id=loop_id ) return ( - compile(tree, filename="", mode="exec"), + compile(tree, filename=fpath, mode="exec"), format_itervars(itervars), found_loop_id, ) @@ -156,17 +157,23 @@ def get_loop_code(loop_frame_info, loop_id): handle_exception(fpath) -def handle_exception(fpath): +def handle_exception(fpath, prefix="_RELOADING_"): + depth = fpath.count(prefix) + mfpath = removePrefix(fpath) exc = traceback.format_exc() - exc = exc.replace('File ""', 'File "{}"'.format(fpath)) - sys.stderr.write(exc + "\n") - print("Edit {} and press return to continue, 'c' for continue, 'e' for exception.".format(fpath)) + exc = exc.replace('File ""', 'File "{}"'.format(mfpath)) + sys.stderr.write(f"Reloading Depth {depth}\n{exc}\n") + hint = "Edit {} and press return to continue" + allow_exception = depth != 0 + if allow_exception: + hint += ", 'e' for exception" + # hint += ", 'k' for skip, 'e' for exception" + hint += "." + print(hint.format(mfpath)) # sys.stdin.readline() signal = input() signal_lower = signal.lower() - if signal_lower == 'c': - return True - elif signal_lower == 'e': + if signal_lower == "e" and allow_exception: raise Exception('Raise exception for file: "{}"'.format(fpath)) else: return False @@ -231,14 +238,26 @@ def isolate_function_def( return False -def get_function_def_code(fpath, fn, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef]): - tree = parse_file_until_successful(fpath) +def removePrefix(fpath, prefix="_RELOADING_"): + mfpath = fpath + while True: + if mfpath.startswith(prefix): + mfpath = mfpath[len(prefix) :] + else: + return mfpath + + +def get_function_def_code( + fpath, fn, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef], prefix="_RELOADING_" +): + mfpath = removePrefix(fpath, prefix=prefix) + tree = parse_file_until_successful(mfpath) found = isolate_function_def(fn.__name__, tree, funcdefs=funcdefs) if not found: return None # print('tree fetched:',tree) # ast.Module object. compiled = compile( - tree, filename=fpath, mode="exec" + tree, filename=prefix + fpath, mode="exec" ) # filename is the same as the original name? # compiled = compile(tree, filename="", mode="exec") # filename is the same as the original name? return compiled @@ -297,7 +316,8 @@ def wrapped(): except Exception: while True: needbreak = handle_exception(fpath) - if needbreak: break + if needbreak: + break try: state["class"] = ( get_reloaded_function( @@ -309,9 +329,10 @@ def wrapped(): ) or state["class"] ) - return state['class'] + return state["class"] except: pass + class_ = wrapped() caller_locals[fn.__name__] = class_ return class_ @@ -356,7 +377,8 @@ def wrapped(*args, **kwargs): return result except Exception: needbreak = handle_exception(fpath) - if needbreak: break + if needbreak: + break state["func"] = ( get_reloaded_function(caller_globals, caller_locals, fpath, fn) or state["func"] From 7481abc3a226ffbec5bc609fe850753c07ab0b77 Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Thu, 10 Nov 2022 19:15:38 +0800 Subject: [PATCH 03/11] disable 'every' parameter for class instance till we have a workaround just set 'every' to 1 for class --- reloading/reloading.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 4a14164..09d9f8f 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -281,18 +281,20 @@ def get_reloaded_function( return func -def _reloading_class(fn, every=1): +def _reloading_class(fn, every=1): # disable the 'every' argument. + every = 1 # override this thing. reload at every time. stack = inspect.stack() # print("stack", stack) # breakpoint() frame, fpath = stack[2][:2] caller_locals = frame.f_locals caller_globals = frame.f_globals - state = { + + # return mclass + state = { # this is not going to preserve the state. "class": None, "reloads": -1, } - # return mclass def wrapped(): state["reloads"] += 1 From 1ae661a2e36311001fb75362963555fdfc5f942f Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Thu, 10 Nov 2022 19:19:43 +0800 Subject: [PATCH 04/11] add prefix for loop code --- reloading/reloading.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 09d9f8f..a1f74a5 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -114,7 +114,7 @@ def isolate_loop_body_and_get_itervars(tree, lineno, loop_id): and node.iter.func.id == "reloading" and ( (loop_id is not None and loop_id == get_loop_id(node)) - or getattr(node, "lineno", None) == lineno + or getattr(node, "lineno", None) == lineno # this is just a hack. ) ): candidate_nodes.append(node) @@ -146,10 +146,12 @@ def get_loop_code(loop_frame_info, loop_id, prefix="_RELOADING_"): tree = parse_file_until_successful(mfpath) try: itervars, found_loop_id = isolate_loop_body_and_get_itervars( - tree, lineno=loop_frame_info[2], loop_id=loop_id + tree, + lineno=loop_frame_info[2], + loop_id=loop_id, # are you sure this will work? ) return ( - compile(tree, filename=fpath, mode="exec"), + compile(tree, filename=prefix + fpath, mode="exec"), format_itervars(itervars), found_loop_id, ) From 031a7ba110d21ef63edc2d84e1cf52b3591d6bff Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Thu, 10 Nov 2022 20:41:21 +0800 Subject: [PATCH 05/11] revoke changes to breakpoint commands --- reloading/reloading.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index a1f74a5..d812699 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -166,16 +166,19 @@ def handle_exception(fpath, prefix="_RELOADING_"): exc = exc.replace('File ""', 'File "{}"'.format(mfpath)) sys.stderr.write(f"Reloading Depth {depth}\n{exc}\n") hint = "Edit {} and press return to continue" - allow_exception = depth != 0 + # allow_exception = depth != 0 + allow_exception = True if allow_exception: - hint += ", 'e' for exception" - # hint += ", 'k' for skip, 'e' for exception" + # hint += ", 'e' for exception" + hint += ", 'k' for skip, 'e' for exception" hint += "." print(hint.format(mfpath)) # sys.stdin.readline() signal = input() signal_lower = signal.lower() - if signal_lower == "e" and allow_exception: + if signal_lower == 'k' and allow_exception: + return True + elif signal_lower == "e" and allow_exception: raise Exception('Raise exception for file: "{}"'.format(fpath)) else: return False From 553f3b737880b887c812bbe4a73cf0633f702163 Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Fri, 11 Nov 2022 00:58:08 +0800 Subject: [PATCH 06/11] set default behavior as reloading on exception --- reloading/reloading.py | 93 +++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index d812699..8e191d0 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -19,6 +19,9 @@ def __iter__(self): ) +from collections import Iterable + + def reloading(fn_or_seq=None, every=1, forever=None): """Wraps a loop iterator or decorates a function to reload the source code before every loop iteration or function invocation. @@ -44,13 +47,19 @@ def reloading(fn_or_seq=None, every=1, forever=None): if fn_or_seq: fntypes = [types.FunctionType] # type(someclass) -> - # print('TYPE:',type(fn_or_seq)) - # breakpoint() - if any(isinstance(fn_or_seq, fntype) for fntype in fntypes): - return _reloading_function(fn_or_seq, every=every) - elif type(fn_or_seq) == type: - return _reloading_class(fn_or_seq, every=every) - return _reloading_loop(fn_or_seq, every=every) + try: + if any(isinstance(fn_or_seq, fntype) for fntype in fntypes): + return _reloading_function(fn_or_seq, every=every) + elif isinstance(fn_or_seq, Iterable): + return _reloading_loop(fn_or_seq, every=every) + else: + return _reloading_class(fn_or_seq, every=every) + except: + import traceback + + traceback.print_exc() + print("UNKNOWN TYPE:", type(fn_or_seq)) + breakpoint() if forever: return _reloading_loop(iter(int, 1), every=every) @@ -170,13 +179,13 @@ def handle_exception(fpath, prefix="_RELOADING_"): allow_exception = True if allow_exception: # hint += ", 'e' for exception" - hint += ", 'k' for skip, 'e' for exception" + hint += ", 'k' for skip, 'e' for exception" # skip is to return None for the current function. maybe it will cause more problems. hint += "." print(hint.format(mfpath)) # sys.stdin.readline() signal = input() signal_lower = signal.lower() - if signal_lower == 'k' and allow_exception: + if signal_lower == "k" and allow_exception: return True elif signal_lower == "e" and allow_exception: raise Exception('Raise exception for file: "{}"'.format(fpath)) @@ -286,7 +295,13 @@ def get_reloaded_function( return func -def _reloading_class(fn, every=1): # disable the 'every' argument. +_reloading_class_dict = {} + + +def _reloading_class( + fn, every=1, reloadOnException=True +): # disable the 'every' argument. + global _reloading_class_dict every = 1 # override this thing. reload at every time. stack = inspect.stack() # print("stack", stack) @@ -296,17 +311,32 @@ def _reloading_class(fn, every=1): # disable the 'every' argument. caller_globals = frame.f_globals # return mclass - state = { # this is not going to preserve the state. - "class": None, - "reloads": -1, - } + _reloading_class_dict[fpath] = _reloading_class_dict.get(fpath, {}) + _reloading_class_dict[fpath][fn] = _reloading_class_dict.get(fpath).get( + fn, + { # this is not going to preserve the state. + "class": None, + "reloads": -1, + }, + ) + # state = _reloading_class_dict[fpath][fn] def wrapped(): - state["reloads"] += 1 while True: try: - if state["reloads"] % every == 0: - state["class"] = ( + if reloadOnException: # should you start a server or something? + if _reloading_class_dict[fpath][fn]["class"] is None: + _reloading_class_dict[fpath][fn][ + "class" + ] = get_reloaded_function( + caller_globals, + caller_locals, + fpath, + fn, + funcdefs=[ast.ClassDef], + ) + elif _reloading_class_dict[fpath][fn]["reloads"] % every == 0: + _reloading_class_dict[fpath][fn]["class"] = ( get_reloaded_function( caller_globals, caller_locals, @@ -314,9 +344,11 @@ def wrapped(): fn, funcdefs=[ast.ClassDef], ) - or state["class"] + or _reloading_class_dict[fpath][fn]["class"] ) - class_ = state["class"] + _reloading_class_dict[fpath][fn]["reloads"] += 1 + + class_ = _reloading_class_dict[fpath][fn]["class"] # the function inside function (closure) is not handled properly. need to decorate again? # do not decorate already decorated function? return class_ @@ -326,7 +358,7 @@ def wrapped(): if needbreak: break try: - state["class"] = ( + _reloading_class_dict[fpath][fn]["class"] = ( get_reloaded_function( caller_globals, caller_locals, @@ -334,9 +366,9 @@ def wrapped(): fn, funcdefs=[ast.ClassDef], ) - or state["class"] + or _reloading_class_dict[fpath][fn]["class"] ) - return state["class"] + return _reloading_class_dict[fpath][fn]["class"] except: pass @@ -345,7 +377,7 @@ def wrapped(): return class_ -def _reloading_function(fn, every=1): +def _reloading_function(fn, every=1, reloadOnException=True): stack = inspect.stack() # what is this stack? # print(stack) @@ -366,18 +398,23 @@ def _reloading_function(fn, every=1): } def wrapped(*args, **kwargs): - if state["reloads"] % every == 0: + if reloadOnException: + if state["func"] is None: + state["func"] = get_reloaded_function( + caller_globals, caller_locals, fpath, fn + ) + elif state["reloads"] % every == 0: state["func"] = ( get_reloaded_function(caller_globals, caller_locals, fpath, fn) or state["func"] ) - state["reloads"] += 1 + state["reloads"] += 1 while True: try: func = state["func"] - print( - f"----\nargs:{args}\nkwargs:{kwargs}\nfunc:{func}\n----" - ) # try to debug. + # print( + # f"----\nargs:{args}\nkwargs:{kwargs}\nfunc:{func}\n----" + # ) # try to debug. # the function inside function (closure) is not handled properly. need to decorate again? # do not decorate already decorated function? result = func(*args, **kwargs) From 67c847df94f18f3b1246782b5210d90feea697a5 Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Mon, 21 Nov 2022 16:22:26 +0800 Subject: [PATCH 07/11] updated latest behavior to inspect the code object retrieve closest function definition according to code object --- reloading/reloading.py | 58 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 8e191d0..42daf3f 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -235,20 +235,43 @@ def strip_reloading_decorator(func): ] -def isolate_function_def( - funcname, tree, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef] +def isolate_function_def( # careful! we need to sort the definition. + funcname, tree, codeInfos,funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef] ): + # if codeInfos is None, then we do not sort. we just return. """Strip everything but the function definition from the ast in-place. Also strips the reloading decorator from the function definition""" + import copy + nodeList = [] for node in ast.walk(tree): if ( any(isinstance(node, funcdef) for funcdef in funcdefs) and node.name == funcname and "reloading" in [get_decorator_name(dec) for dec in node.decorator_list] ): - strip_reloading_decorator(node) - tree.body = [node] - return True + # please sort it in some way. + # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_attributes', '_fields', 'args', 'body', 'col_offset', 'decorator_list', 'end_col_offset', 'end_lineno', 'lineno', 'name', 'returns', 'type_comment'] + # print("NODE?",node) + # breakpoint() + if codeInfos is None: + strip_reloading_decorator(node) + tree.body = [node] + return True + else: + lineno = node.lineno + # sort it out please! + addrDistance = abs(codeInfos["lineNumber"] - lineno) + nodeList.append( + (addrDistance, + copy.copy(node)) # append what? copy what? sort what? + ) + nodeList.sort(key=lambda x: x[0]) + if codeInfos is not None and nodeList != []: + # return nodeList[0][1] + node = nodeList[0][1] + strip_reloading_decorator(node) + tree.body = [node] + return True return False @@ -266,7 +289,22 @@ def get_function_def_code( ): mfpath = removePrefix(fpath, prefix=prefix) tree = parse_file_until_successful(mfpath) - found = isolate_function_def(fn.__name__, tree, funcdefs=funcdefs) + ## locate the freaking function definition! + # print(dir(fn)) + funcString = str(fn.__code__) + # + # what is the damn location of this code object? + import parse + # codeFormat = '' + # codeFormat = '' + # codeFormat = '' + codeFormat = '' + codeInfos = parse.parse(codeFormat, funcString) + # + # print(codeInfos) # None? + # print([funcString]) + # breakpoint() + found = isolate_function_def(fn.__name__, tree, codeInfos,funcdefs=funcdefs) if not found: return None # print('tree fetched:',tree) # ast.Module object. @@ -386,6 +424,14 @@ def _reloading_function(fn, every=1, reloadOnException=True): # stack[1] -> reloading function # stack[2] -> original function # FrameInfo(frame=>, filename='/media/root/parrot/pyjom/tests/skipexception_code_and_continue_resurrection_time_travel_debugging/hook_error_handler_to_see_if_context_preserved.py', lineno=67, function='', code_context=['def anotherFunction():\n'], index=0) + # what the fuck is this stack[2]? let's see. + ########################## + # seems this is the damn function... yeah... + # FrameInfo(frame=>, filename='reload_py_template.py', lineno=4, function='', code_context=['def testfunc():\n'], index=0) + # mstack = stack[2] + # print(mstack) + # breakpoint() + ########################## frame, fpath = stack[2][:2] caller_locals = frame.f_locals caller_globals = frame.f_globals From 70a8bbcf9c3d2e09d5303ad81cde94e70490facd Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Mon, 21 Nov 2022 21:04:09 +0800 Subject: [PATCH 08/11] update logic to locate function/class definition --- reloading/reloading.py | 105 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 42daf3f..8f60a2b 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -7,6 +7,62 @@ from functools import partial, update_wrapper +def getCodeInfoFromCodeObject(maybeCodeObject): + mdict = dir(maybeCodeObject) + if "__code__" in mdict: + mycode = maybeCodeObject.__code__ + else: + mycode = maybeCodeObject + # print("MYCODE?",mycode) # it is function, not code object. fucking shit. + # breakpoint() + + # funcString = str(fn.__code__) + funcString = str(mycode) + # + # what is the damn location of this code object? + import parse + # codeFormat = '' + # codeFormat = '' + # codeFormat = '' + codeFormat = '' + codeInfos = parse.parse(codeFormat, funcString) + return codeInfos + +def getMyIndexFromStackForFn(stack, fn, codeInfos): + msortedIndexs = [] + # print("CODEINFOS?", codeInfos) # it is really None. fucking shit. + for mindex in range(0, min(len(stack), 10)): + score = 0 + mstack = stack[mindex] + code_context = " ".join(mstack.code_context) # this is list. + # print("NAME?",fn.__name__) + # print("CONTEXT?",code_context) # this is a list! + if fn.__name__ not in code_context: + score+=5000 # this is the main signal. fuck. + lineno = mstack.lineno + if codeInfos is not None: + mlineno = codeInfos['lineNumber'] + mlineDistance = abs(mlineno-lineno) + score+=mlineDistance + filename = mstack.filename + if codeInfos is not None: + # remove prefixes? + import os + fname_0 = removePrefix(os.path.split(filename)[-1]) + fname_1 = removePrefix(os.path.split(codeInfos['fileName'])[-1]) + # print("FNAME_0?", fname_0) + # print("FNAME_1?", fname_1) + if fname_0!=fname_1: + score+=50000 + msortedIndexs.append((mindex, score)) + + msortedIndexs.sort(key=lambda x: x[1]) + # print("SORTED INDEXS?") + # print(msortedIndexs) + + myindex = msortedIndexs[0][0] + return myindex + # have to make our own partial in case someone wants to use reloading as a iterator without any arguments # they would get a partial back because a call without a iterator argument is assumed to be a decorator. # getting a "TypeError: 'functools.partial' object is not iterable" @@ -291,15 +347,8 @@ def get_function_def_code( tree = parse_file_until_successful(mfpath) ## locate the freaking function definition! # print(dir(fn)) - funcString = str(fn.__code__) - # - # what is the damn location of this code object? - import parse - # codeFormat = '' - # codeFormat = '' - # codeFormat = '' - codeFormat = '' - codeInfos = parse.parse(codeFormat, funcString) + # TODO: parse line number and code metadata from stringified code object + codeInfos = getCodeInfoFromCodeObject(fn) # # print(codeInfos) # None? # print([funcString]) @@ -344,7 +393,13 @@ def _reloading_class( stack = inspect.stack() # print("stack", stack) # breakpoint() - frame, fpath = stack[2][:2] + + codeInfos = getCodeInfoFromCodeObject(fn) + + myindex = getMyIndexFromStackForFn(stack, fn, codeInfos) + frame, fpath = stack[myindex][:2] + # frame, fpath = stack[2][:2] # what exactly is the damn stack? + # reload class? wtf? caller_locals = frame.f_locals caller_globals = frame.f_globals @@ -415,7 +470,7 @@ def wrapped(): return class_ -def _reloading_function(fn, every=1, reloadOnException=True): +def _reloading_function(fn, every=1, reloadOnException=True, ): stack = inspect.stack() # what is this stack? # print(stack) @@ -432,7 +487,31 @@ def _reloading_function(fn, every=1, reloadOnException=True): # print(mstack) # breakpoint() ########################## - frame, fpath = stack[2][:2] + # the freaking index + codeInfos = getCodeInfoFromCodeObject(fn) + + myindex = getMyIndexFromStackForFn(stack, fn, codeInfos) + # print('INDEX?', myindex) # why the fucking index is zero? + + ## TODO: get this fucking index. + + frame, fpath = stack[myindex][:2] # myindex is usually 2. + + # if containing shit, please do something about it. + # if code_context does not contain the function name, add fucking score. + # select closest stack frame. + # if do not contain given file name, add more fucking score. + # sort things out, with relevancy first, index order later. + + # FRAME? > + # you may want to use this frame. + # method for finding this frame at spot? + print("____") + # FrameInfo(frame=>, filename='reload_py_template.py', lineno=4, function='', code_context=['def testfunc():\n'], index=0) + # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_asdict', '_field_defaults', '_fields', '_fields_defaults', '_make', '_replace', 'code_context', 'count', 'filename', 'frame', 'function', 'index', 'lineno'] + # print("FRAME?", frame) + # print("FPATH?", fpath) + # breakpoint() caller_locals = frame.f_locals caller_globals = frame.f_globals # 'clear', 'f_back', 'f_builtins', 'f_code', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_trace', 'f_trace_lines', 'f_trace_opcodes' @@ -463,7 +542,7 @@ def wrapped(*args, **kwargs): # ) # try to debug. # the function inside function (closure) is not handled properly. need to decorate again? # do not decorate already decorated function? - result = func(*args, **kwargs) + result = func(*args, **kwargs) # NONE? WTF? return result except Exception: needbreak = handle_exception(fpath) From 8e551004937a9b833888e67edd9a8bfb796495ec Mon Sep 17 00:00:00 2001 From: James4Ever0 Date: Tue, 22 Nov 2022 23:23:49 +0800 Subject: [PATCH 09/11] modify for hy source code reloading must use my modified hy version, the stock version will not work. --- reloading/reloading.py | 130 ++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 8f60a2b..5eefa3d 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -21,6 +21,7 @@ def getCodeInfoFromCodeObject(maybeCodeObject): # # what is the damn location of this code object? import parse + # codeFormat = '' # codeFormat = '' # codeFormat = '' @@ -28,32 +29,34 @@ def getCodeInfoFromCodeObject(maybeCodeObject): codeInfos = parse.parse(codeFormat, funcString) return codeInfos + def getMyIndexFromStackForFn(stack, fn, codeInfos): msortedIndexs = [] # print("CODEINFOS?", codeInfos) # it is really None. fucking shit. for mindex in range(0, min(len(stack), 10)): score = 0 mstack = stack[mindex] - code_context = " ".join(mstack.code_context) # this is list. + code_context = " ".join(mstack.code_context) # this is list. # print("NAME?",fn.__name__) # print("CONTEXT?",code_context) # this is a list! if fn.__name__ not in code_context: - score+=5000 # this is the main signal. fuck. + score += 5000 # this is the main signal. fuck. lineno = mstack.lineno if codeInfos is not None: - mlineno = codeInfos['lineNumber'] - mlineDistance = abs(mlineno-lineno) - score+=mlineDistance + mlineno = codeInfos["lineNumber"] + mlineDistance = abs(mlineno - lineno) + score += mlineDistance filename = mstack.filename if codeInfos is not None: # remove prefixes? import os - fname_0 = removePrefix(os.path.split(filename)[-1]) - fname_1 = removePrefix(os.path.split(codeInfos['fileName'])[-1]) + + fname_0 = removePrefix(os.path.split(filename)[-1]) + fname_1 = removePrefix(os.path.split(codeInfos["fileName"])[-1]) # print("FNAME_0?", fname_0) # print("FNAME_1?", fname_1) - if fname_0!=fname_1: - score+=50000 + if fname_0 != fname_1: + score += 50000 msortedIndexs.append((mindex, score)) msortedIndexs.sort(key=lambda x: x[1]) @@ -63,6 +66,7 @@ def getMyIndexFromStackForFn(stack, fn, codeInfos): myindex = msortedIndexs[0][0] return myindex + # have to make our own partial in case someone wants to use reloading as a iterator without any arguments # they would get a partial back because a call without a iterator argument is assumed to be a decorator. # getting a "TypeError: 'functools.partial' object is not iterable" @@ -114,7 +118,7 @@ def reloading(fn_or_seq=None, every=1, forever=None): import traceback traceback.print_exc() - print("UNKNOWN TYPE:", type(fn_or_seq)) + print("UNKNOWN TYPE:", type(fn_or_seq)) # wtf is this? breakpoint() if forever: return _reloading_loop(iter(int, 1), every=every) @@ -157,11 +161,40 @@ def load_file(path): return src + "\n" -def parse_file_until_successful(path): +def parse_file_until_successful(path, module_name = "__main__"): # what is this fucking file? source = load_file(path) while True: try: - tree = ast.parse(source) + # treat shit differently. please! + if path.endswith(".hy"): # but we want the fucking module name? + # do different things! + # use module_name to do the shit. + import io + mstream = io.StringIO(source) + from hy.reader import HyReader + mgen = HyReader().parse(mstream, path) + # please do something to do the fucking config. + from hy.config import config + import hy.models + # default? + # 'toplevel':True,'line-by-line':True,'disable-showstack':False + hst = hy.models.Lazy( # how to use this fucking shit? + gen=mgen, + stream=mstream, + filename=path, + temaps={} if config['line-by-line'] else None, + protect_toplevel=True if config['toplevel'] else False, + disable_showstack=True if config['disable-showstack'] else False, # these configs can be retrieved from hy.config. + ) + hst.source = source + hst.filename = path + from hy.compiler import hy_compile + + tree = hy_compile( + hst, module_name, filename=path, source=source # man you need to get the fucking module name. + )# does this fucking works? + else: + tree = ast.parse(source) return tree except SyntaxError: handle_exception(path) @@ -176,39 +209,44 @@ def isolate_loop_body_and_get_itervars(tree, lineno, loop_id): if ( isinstance(node, ast.For) and isinstance(node.iter, ast.Call) - and node.iter.func.id == "reloading" + and node.iter.func.id == "reloading" # what the fuck is this shit? and ( (loop_id is not None and loop_id == get_loop_id(node)) or getattr(node, "lineno", None) == lineno # this is just a hack. ) ): - candidate_nodes.append(node) - - if len(candidate_nodes) > 1: - raise LookupError( - "The reloading loop is ambigious. Use `reloading` only once per line and make sure that the code in that line is unique within the source file." - ) - + dis = abs(getattr(node, "lineno", 0) - lineno) + candidate_nodes.append((node, dis)) + + # please sort by the fucking line no. + # if len(candidate_nodes) > 1: + # raise LookupError( + # "The reloading loop is ambigious. Use `reloading` only once per line and make sure that the code in that line is unique within the source file." + # ) if len(candidate_nodes) < 1: raise LookupError( "Could not locate reloading loop. Please make sure the code in the line that uses `reloading` doesn't change between reloads." ) + candidate_nodes.sort(key=lambda x: x[1]) # shall you fucking know the damn name? or some - loop_node = candidate_nodes[0] + loop_node, _ = candidate_nodes[0] + # loop_node = candidate_nodes[0] tree.body = loop_node.body return loop_node.target, get_loop_id(loop_node) -def get_loop_id(ast_node): +def get_loop_id(ast_node): # wtf is that shit? """Generates a unique identifier for an `ast_node` of type ast.For to find the loop in the changed source file""" return ast.dump(ast_node.target) + "__" + ast.dump(ast_node.iter) def get_loop_code(loop_frame_info, loop_id, prefix="_RELOADING_"): - fpath = loop_frame_info[1] + fpath = loop_frame_info[1] # what the fuck is this frame loop? + # what is the fucking module name? + module_name = "__main__" # just fuck it! mfpath = removePrefix(fpath, prefix=prefix) while True: - tree = parse_file_until_successful(mfpath) + tree = parse_file_until_successful(mfpath, module_name=module_name) try: itervars, found_loop_id = isolate_loop_body_and_get_itervars( tree, @@ -250,7 +288,7 @@ def handle_exception(fpath, prefix="_RELOADING_"): def _reloading_loop(seq, every=1): - loop_frame_info = inspect.stack()[2] + loop_frame_info = inspect.stack()[2] # man fucking careful about this shit! why this stack is fucked? fpath = loop_frame_info[1] caller_globals = loop_frame_info[0].f_globals @@ -291,13 +329,14 @@ def strip_reloading_decorator(func): ] -def isolate_function_def( # careful! we need to sort the definition. - funcname, tree, codeInfos,funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef] +def isolate_function_def( # careful! we need to sort the definition. + funcname, tree, codeInfos, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef] ): # if codeInfos is None, then we do not sort. we just return. """Strip everything but the function definition from the ast in-place. Also strips the reloading decorator from the function definition""" import copy + nodeList = [] for node in ast.walk(tree): if ( @@ -305,8 +344,8 @@ def isolate_function_def( # careful! we need to sort the definition. and node.name == funcname and "reloading" in [get_decorator_name(dec) for dec in node.decorator_list] ): - # please sort it in some way. - # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_attributes', '_fields', 'args', 'body', 'col_offset', 'decorator_list', 'end_col_offset', 'end_lineno', 'lineno', 'name', 'returns', 'type_comment'] + # please sort it in some way. + # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_attributes', '_fields', 'args', 'body', 'col_offset', 'decorator_list', 'end_col_offset', 'end_lineno', 'lineno', 'name', 'returns', 'type_comment'] # print("NODE?",node) # breakpoint() if codeInfos is None: @@ -318,8 +357,10 @@ def isolate_function_def( # careful! we need to sort the definition. # sort it out please! addrDistance = abs(codeInfos["lineNumber"] - lineno) nodeList.append( - (addrDistance, - copy.copy(node)) # append what? copy what? sort what? + ( + addrDistance, + copy.copy(node), + ) # append what? copy what? sort what? ) nodeList.sort(key=lambda x: x[0]) if codeInfos is not None and nodeList != []: @@ -340,11 +381,12 @@ def removePrefix(fpath, prefix="_RELOADING_"): return mfpath -def get_function_def_code( +def get_function_def_code( # please! use this against your fucking hy code! fpath, fn, funcdefs=[ast.FunctionDef, ast.AsyncFunctionDef], prefix="_RELOADING_" ): - mfpath = removePrefix(fpath, prefix=prefix) - tree = parse_file_until_successful(mfpath) + mfpath = removePrefix(fpath, prefix=prefix) # please consider hy code! + module_name = getattr(fn,"__module__", "__main__") + tree = parse_file_until_successful(mfpath, module_name=module_name) ## locate the freaking function definition! # print(dir(fn)) # TODO: parse line number and code metadata from stringified code object @@ -353,7 +395,7 @@ def get_function_def_code( # print(codeInfos) # None? # print([funcString]) # breakpoint() - found = isolate_function_def(fn.__name__, tree, codeInfos,funcdefs=funcdefs) + found = isolate_function_def(fn.__name__, tree, codeInfos, funcdefs=funcdefs) if not found: return None # print('tree fetched:',tree) # ast.Module object. @@ -397,7 +439,8 @@ def _reloading_class( codeInfos = getCodeInfoFromCodeObject(fn) myindex = getMyIndexFromStackForFn(stack, fn, codeInfos) - frame, fpath = stack[myindex][:2] + mstack = stack[myindex] + frame, fpath = mstack[:2] # frame, fpath = stack[2][:2] # what exactly is the damn stack? # reload class? wtf? caller_locals = frame.f_locals @@ -470,7 +513,11 @@ def wrapped(): return class_ -def _reloading_function(fn, every=1, reloadOnException=True, ): +def _reloading_function( + fn, + every=1, + reloadOnException=True, +): stack = inspect.stack() # what is this stack? # print(stack) @@ -490,12 +537,15 @@ def _reloading_function(fn, every=1, reloadOnException=True, ): # the freaking index codeInfos = getCodeInfoFromCodeObject(fn) - myindex = getMyIndexFromStackForFn(stack, fn, codeInfos) + myindex = getMyIndexFromStackForFn(stack, fn, codeInfos) # why i cannot obtain the fucking __module__ name? wtf? # print('INDEX?', myindex) # why the fucking index is zero? ## TODO: get this fucking index. + mstack = stack[myindex] + # print('MSTACK?', mstack) + # breakpoint() - frame, fpath = stack[myindex][:2] # myindex is usually 2. + frame, fpath = mstack[:2] # myindex is usually 2. # if containing shit, please do something about it. # if code_context does not contain the function name, add fucking score. @@ -542,7 +592,7 @@ def wrapped(*args, **kwargs): # ) # try to debug. # the function inside function (closure) is not handled properly. need to decorate again? # do not decorate already decorated function? - result = func(*args, **kwargs) # NONE? WTF? + result = func(*args, **kwargs) # NONE? WTF? return result except Exception: needbreak = handle_exception(fpath) From d32cd048ba22e37d70eeef2ba34194c34521d621 Mon Sep 17 00:00:00 2001 From: modifier Date: Thu, 24 Nov 2022 19:25:05 +0800 Subject: [PATCH 10/11] fuck --- reloading/reloading.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reloading/reloading.py b/reloading/reloading.py index 5eefa3d..997b24c 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -78,8 +78,10 @@ def __iter__(self): "Nothing to iterate over. Please pass an iterable to reloading." ) - -from collections import Iterable +try: + from collections import Iterable +except: + from collections.abc import Iterable def reloading(fn_or_seq=None, every=1, forever=None): From 907886fcb451dabcefc323066362cba0b734c4f1 Mon Sep 17 00:00:00 2001 From: modifier Date: Fri, 25 Nov 2022 19:35:31 +0800 Subject: [PATCH 11/11] init --- justpush.sh | 3 +++ reloading/reloading.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 justpush.sh diff --git a/justpush.sh b/justpush.sh new file mode 100644 index 0000000..79c3103 --- /dev/null +++ b/justpush.sh @@ -0,0 +1,3 @@ +git add . +git commit -m "init" +git push origin master diff --git a/reloading/reloading.py b/reloading/reloading.py index 997b24c..4f6a00f 100644 --- a/reloading/reloading.py +++ b/reloading/reloading.py @@ -519,6 +519,7 @@ def _reloading_function( fn, every=1, reloadOnException=True, + debug=False ): stack = inspect.stack() # what is this stack? @@ -558,7 +559,9 @@ def _reloading_function( # FRAME? > # you may want to use this frame. # method for finding this frame at spot? - print("____") + # wtf is this shit? + if debug: + print("____") # FrameInfo(frame=>, filename='reload_py_template.py', lineno=4, function='', code_context=['def testfunc():\n'], index=0) # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_asdict', '_field_defaults', '_fields', '_fields_defaults', '_make', '_replace', 'code_context', 'count', 'filename', 'frame', 'function', 'index', 'lineno'] # print("FRAME?", frame)