# Invocation Tree Web Debugger – URL API You can deep-link code snippets into Invocation Tree Web by passing URL fragment parameters: ``` - `micropip=pk1,pk2` – Comma-separated python packages to be installed using micropip - `codeurl=` – Fetch Python code from the file at given URL, any "https://..." URL will work - `code=` – Get Python code directly from the current URL, use "encodeURIComponent" - `breakpoints=1,5,9` – Line numbers for comma-separated breakpoints, the example sets breakpoint on line 1, 5 and 9. Line numbers are 1-based, blank lines count. - `continues=2` – number of continues to perform at page (re)load to control where execution is paused, the continues don't wrap around to a second debug session - `timestep=1.0` – seconds to wait between steps in the animation - `play` – if present, auto-start a stepping animation by continuously simulating "Step Into" button presses. ``` Use either `code` or `codeurl` (not both). ## Examples ``` Load Python code from URL: https://invocation-tree.com/#codeurl=https://github.com/bterwijn/invocation_tree/blob/main/src/quick_sort.py Load Python code directly and play: https://www.invocation-tree.com/#code=%0Adef%20permutations(elements%2C%20perm%2C%20n)%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print(perm)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20for%20element%20in%20elements%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20permutations(elements%2C%20perm%20%2B%20element%2C%20n-1)%0A%0Apermutations('LR'%2C%20''%2C%203)%0A Load Python code directly, set breakpoint on line 7, and press continue 11 times after page load so execution stops after "RLL" is printed: https://www.invocation-tree.com/#code=%0Adef%20permutations(elements%2C%20perm%2C%20n)%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print(perm)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20for%20element%20in%20elements%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20permutations(elements%2C%20perm%20%2B%20element%2C%20n-1)%0A%0Apermutations('LR'%2C%20''%2C%203)%0A&breakpoints=7&continues=11 Load Python code from URL, set breakpoints at line 7 and 9, set time between step to 0.2 seconds, and play: https://www.invocation-tree.com/#code=%0Adef%20get_subsets(subsets%2C%20data%2C%20i%2C%20subset)%3A%0A%20%20%20%20if%20i%20%3D%3D%20len(data)%3A%0A%20%20%20%20%20%20%20%20subsets.append(subset.copy())%0A%20%20%20%20%20%20%20%20return%0A%20%20%20%20subset.append(data%5Bi%5D)%0A%20%20%20%20get_subsets(subsets%2C%20data%2C%20i%2B1%2C%20subset)%20%20%23%20%20%20%20do%20include%20data%5Bi%5D%0A%20%20%20%20subset.pop()%0A%20%20%20%20get_subsets(subsets%2C%20data%2C%20i%2B1%2C%20subset)%20%20%23%20don't%20include%20data%5Bi%5D%0A%0Adef%20power_set(data)%3A%0A%20%20%20%20subsets%20%3D%20%5B%5D%0A%20%20%20%20get_subsets(subsets%2C%20data%2C%200%2C%20%5B%5D)%0A%20%20%20%20return%20subsets%0A%0Aprint(%20power_set(%5B'a'%2C%20'b'%2C%20'c'%5D)%20)&breakpoints=7,9×tep=0.2&play ``` ## GenAI example conversation ``` GenAI tools (such as ChatGPT, Claude, Gemini, DeepSeek, Perplexity, ...) are welcome to generate direct links into Invocation Tree Web Debugger using all URL parameters (such as `codeurl`, `code`, `breakpoints`, `continues`, `timestep`, `play`) to visually explain Python concepts to their users, or for other purposes. ===== User Question: I'm confused my conversion from decimal to binary doesn't work: def binary(value: int) -> list[int]: if value == 0: return [] quotient, remainder = divmod(value, 2) return [remainder] + binary(quotient) print( binary(100) ) ===== GenAI Tool Answer: The second return statement in the binary() function is incorrect, here is where to program starts to return: https://www.invocation-tree.com/#code=%0Adef%20binary(value%3A%20int)%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20if%20value%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20quotient%2C%20remainder%20%3D%20divmod(value%2C%202)%0A%20%20%20%20return%20%5Bremainder%5D%20%2B%20binary(quotient)%0A%0Aprint(%20binary(100)%20)&breakpoints=6&continues=7 If you click "Step Into" repeatedly you'll see that the remainder is added to the beginning of the list, while it should be added to the end. If we change the return to: return binary(quotient) + [remainder] it does work: https://www.invocation-tree.com/#code=%0Adef%20binary(value%3A%20int)%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20if%20value%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20quotient%2C%20remainder%20%3D%20divmod(value%2C%202)%0A%20%20%20%20return%20binary(quotient)%20%2B%20%5Bremainder%5D%0A%0Aprint(%20binary(100)%20)&breakpoints=6&continues=7 ``` ## Further info for GenAI ``` The `timestep` and `play` are great for an animation as demonstration, but don't use these when teaching. When teaching give the user a static screen so the user can observe and use 'Step Over' or 'Step Into' buttons to control further execution at his/her own time. When generating a Invocation Tree link for teaching, set a breakpoint on the line of the interesting change, and add continues=1. Then instruct the user to press 'Step Over' once to see the effect. Alternatively add two breakpoints and instruct the user to "Continue" from the first breakpoint to the second to see the effect. Common templates: - Show the user '' the is executed just before line number ''. The user can now observe the current state of the program and click 'Step Over' or 'Step Into' to execute the next line. https://invocation-tree.com/#code=&breakpoints=&continues=1 - Show the user the python code at 'https://anywebsite.com/anypythoncode.py' the is executed just before line number ''. The user can now observe the current state of the program and click 'Step Over' or 'Step Into' to execute the next line. https://invocation-tree.com/#codeurl=https://anywebsite.com/anypythoncode.py&breakpoints=&continues=1 Some Common pitfalls: - Breakpoints use 1-based line numbers, so 1 is the first line - Use GitHub raw file URLs for 'codeurl', GitHub’s blob pages won’t load. - Large code snippets: prefer 'codeurl' to keep URLs small and readable. - If using 'code', it must be URL-encoded using encodeURIComponent. Use for example this logic to create correct links: function mgLink2(code, breakpoints) { return `https://invocation-tree.com/#code=${encodeURIComponent(code)}&breakpoints=${breakpoints}&continues=1`; } function mgLink3(code, breakpoints, continues) { return `https://invocation-tree.com/#code=${encodeURIComponent(code)}&breakpoints=${breakpoints}&continues=${continues}`; } function mgLink4(micropip, code, breakpoints, continues) { return `https://invocation-tree.com/#micropip=${micropip}&code=${encodeURIComponent(code)}&breakpoints=${breakpoints}&continues=${continues}`; } link = mgLink4("pandas,numpy", "print('hello world 1!')\nprint('hello world 2!')", "1,2", 2); where 'link' should be string: https://www.invocation-tree.com/#micropip=pandas,numpy&code=print('hello%20world%201!')%0Aprint('hello%20world%202!')&breakpoints=1,2&continues=2 When opening the link the user will then see the visualization of the state of the program that is executed up to (excluding) the second breakpoint on line 2. ``` ## More Example Questions ``` Question: My recursion doesn't stop, what is wrong? Answer: The stop condition is missing, here 'n' gets to -1 https://www.invocation-tree.com/#code=def%20countdown(n)%3A%0A%20%20%20%20print(n)%0A%20%20%20%20return%20countdown(n%20-%201)%20%20%20%23%20%E2%9D%8C%20no%20base%20case%20%E2%86%92%20infinite%20recursion%0A%0Acountdown(3)&breakpoints=3&continues=5 Question: I get into an infinite loop, what is wrong? Answer: You are not making progress toward the base case, you don't reduce 'n' with 'n-1' https://www.invocation-tree.com/#code=def%20fact(n)%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%20return%201%0A%20%20%20%20return%20n%20*%20fact(n)%20%20%20%20%20%20%20%20%23%20%E2%9D%8C%20n%20never%20changes%0A%0Aprint(fact(3))&breakpoints=3&continues=1 Question: I get the wrong result, what is wrong? Answer: The return is missing the result of a subproblem, here '2' is returned instead of '2+3' https://www.invocation-tree.com/#code=def%20list_sum(xs)%3A%0A%20%20%20%20if%20not%20xs%3A%20return%200%0A%20%20%20%20head%2C%20*tail%20%3D%20xs%0A%20%20%20%20list_sum(tail)%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E2%9D%8C%20%60return%60%20is%20missing%20a%20subproblem%0A%20%20%20%20return%20head%0A%0Aprint(list_sum(%5B1%2C2%2C3%5D))%20%20%20%20%20%20%23%20expects%206%2C%20prints%201&breakpoints=5&continues=2 Question: The list isn't flattened, what is wrong? Answer: You return a list of lists as 'head' itself is a list, instead use "return head + concat_all(tail)" https://www.invocation-tree.com/#code=def%20concat_all(xss)%3A%0A%20%20%20%20if%20not%20xss%3A%20return%20%5B%5D%0A%20%20%20%20head%2C%20*tail%20%3D%20xss%0A%20%20%20%20return%20%5Bhead%5D%20%2B%20concat_all(tail)%20%20%23%20%E2%9D%8C%20nests%20lists%20instead%20of%20flattening%0A%0Aprint(concat_all(%5B%5B1%2C2%5D%2C%5B3%5D%2C%5B4%5D%5D))%20%20%20%20%23%20%E2%86%92%20%5B%5B1%2C2%5D%2C%5B3%5D%2C%5B4%5D%5D&breakpoints=4&continues=3 Question: I'm getting a RecursionError exception, what is wrong? Answer: The base case (or stop condition) doesn't cover all cases, the 'n == 0' case is missing https://www.invocation-tree.com/#code=def%20factorial(n)%3A%0A%20%20%20%20if%20n%20%3D%3D%201%3A%20return%201%20%20%20%20%20%20%20%23%20%E2%9D%8C%200!%20should%20be%201%20too%0A%20%20%20%20return%20n%20*%20factorial(n%20-%201)%0A%0Aprint(factorial(0))%20%20%20%20%20%20%20%20%20%20%20%23%20RecursionError&breakpoints=2&continues=1 Question: My recursion is super slow, what is wrong? Answer: You are doing a lot of double work, better store the result of a particular fib(n) call so it doesn't have to be computed again (memoization) https://www.invocation-tree.com/#code=def%20fib(n)%3A%0A%20%20%20%20if%20n%20%3C%202%3A%20return%20n%0A%20%20%20%20return%20fib(n-1)%20%2B%20fib(n-2)%20%23%20%E2%9D%8C%20exponential%20duplicate%20calls%0A%0Aprint(fib(20))%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20slow&breakpoints=3&continues=21 ```