aroberge / reeborg

Enhanced Karel-the-robot clone
http://reeborg.ca/reeborg.html
Other
47 stars 36 forks source link

tracing/stepping with variable watch #196

Closed dz0 closed 8 years ago

dz0 commented 9 years ago

locals() and globals() are available in Brython -- so one can show them on each step.

ps.: https://github.com/amrdraz/python-debugger is designed for Brython and probably is better than the one Reeborg uses now :)

aroberge commented 9 years ago

The way I evaluate the code is effectively to do eval(python_code). Brython's eval first transforms the python_code into javascript and then runs it: there is no stepping through the code. When an "atomic" javascript instruction is encountered, such as move(), its result is recorded in a "frame" where I record the entire state of the world at that point. When all the code execution is done, a series of such frames will have been recorded.

So, no stepping of the code takes place, and neither Brython's locals() nor globals() are necessarily available at a given step. If you recall,you can choose either Python or Javascript (or Coffeescript) as programming language.

After this code execution is done, I play back the frames one at a time with a specified time delay. The instruction "think(delay)" can be used in a given frame to change the delay between frames being shown from that point on.

The Python debugger you found looks like something extremely worthwhile to look at ... but it would mean changing significantly the way the program is run (and would likely not be compatible with the Javascript or Coffeescript implementation).

dz0 commented 9 years ago

Probably what I call "stepping", is called "line highlighting" in Reeborg.

My idea/hack would be to inject variable "logging" alongside line highlighting

tracecall_name = 'print(list(locals().keys())); RUR.set_lineno_highlight'
aroberge commented 9 years ago

Oh, I see ... this makes sense, of course! It's definitely something I will want to explore.

aroberge commented 8 years ago

EDIT: narration (mentioned previously in this comment) has been changed to print_html; my plan is to enhance print_html.

I could not get something sensible that works. However, I could define a "watch" function, that would need to be called explicitly, and working like the following:

World("Empty")
no_highlight()
think(0)

r = UsedRobot()
def watch(*args):
    out = []
    for arg in args:
        try:
            out.append("{}: {}".format(arg, eval(arg)))
        except:
            pass
    print_html("<br>".join(out))

for i in range(3):
    move()
    watch("r.body.x", "r.body.y", "a")
    a = 3
    pause(1000)

Perhaps I could create a dialog where the variable to watch are entered; when this is done, then a call to watch() is inserted in the same way I enter the code to highlight lines of code being executed...

dz0 commented 8 years ago

Strange issue with locals()

But in their demo env this works ok:

print( locals() )

http://brython.info/tests/editor.html?lang=en Brython version: 3.2.3

but Reeborg uses a bit recent version -- maybe there's some bug? http://cdn.rawgit.com/brython-dev/brython/3c551ae3bf6d29191f7f62bf0d2ce987d70fc340/www/src/brython.js // implementation [3, 2, 4, 'alpha', 0]

ps.: adding watch variables via dialog is also a good way, imo :) even easier to use might be a textbox, where one could list them (add or delete)

On Sat, Dec 5, 2015 at 3:51 PM, André Roberge notifications@github.com wrote:

I could not get something sensible that works. However, I could define a "watch" function, that would need to be called explicitly, and working like the following:

World("Empty") no_highlight() think(0)

r = UsedRobot()def watch(*args): out = [] for arg in args: try: out.append("{}: {}".format(arg, eval(arg))) except: pass narration("
".join(out)) for i in range(3): move() watch("r.body.x", "r.body.y", "a") a = 3 pause(1000)

Perhaps I could create a dialog where the variable to watch are entered; when this is done, then a call to watch() is inserted in the same way I enter the code to highlight lines of code being executed...

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162188747.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

aroberge commented 8 years ago

The reason why locals() is behaving strangely I suspect is that I run the code using exec(src, some_dict) inside a function named generic_translation_python which prepopulates some_dict with some custom-defined functions, instead of a simple exec(src) which would only use the built-ins.

aroberge commented 8 years ago

I should add: Brython's 3.2.3 has bugs which I found and were affecting the way my program was working - which is why I linked to a newer version that had those bugs fixed.

dz0 commented 8 years ago

hm, now world.html throws errors,

but in world_old.html I can get list of local vars

print(list(locals().keys()))

http://reeborg.ca/world_old.html?proglang=python-en&world=%7B%22robots%22%3A%5B%7B%22x%22%3A2%2C%22y%22%3A1%2C%22tokens%22%3A%22infinite%22%2C%22orientation%22%3A0%2C%22_prev_x%22%3A1%2C%22_prev_y%22%3A1%2C%22_prev_orientation%22%3A0%7D%5D%2C%22walls%22%3A%7B%7D%7D&editor=move()%0Aprint(list(locals().keys()))&library=%23%20'import%20my_lib'%20in%20Python%20Code%20is%20required%20to%20use%0A%23%20the%20code%20in%20this%20library.%20%0A%0A

On Sat, Dec 5, 2015 at 11:23 PM, André Roberge notifications@github.com wrote:

I should add: Brython's 3.2.3 has bugs which I found and were affecting the way my program was working - which is why I linked to a newer version that had those bugs fixed.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162249026.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

aroberge commented 8 years ago

I have changed the UI for this in two ways: adding placeholder text in the input which, hopefully should be nearly self-explanatory; using only one image/button which is used to toggle the input: if the input is invisible, no variable is watched. The previous "red magnifying glass" could be confusing: does pressing the red button means stop ... or does it mean that something is stopped?

dz0 commented 8 years ago

seems nice! :) few notices:

On Sun, Dec 6, 2015 at 4:52 PM, André Roberge notifications@github.com wrote:

I have changed the UI for this in two ways: adding placeholder text in the input which, hopefully should be nearly self-explanatory; using only one image/button which is used to toggle the input: if the input is invisible, no variable is watched. The previous "red magnifying glass" could be confusing: does pressing the red button means stop ... or does it mean that something is stopped?

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162322034.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

dz0 commented 8 years ago

PS.: seems like I managed to get the list of system variables:

system_default_vars = list(locals().keys()) # before the user code

def filter_user_vars( vars ): return set( vars ) - set( system_default_vars )

watch ( filter_user_vars( list(locals().keys()) ) ) # would call watch with just vars from user code...

On Sun, Dec 6, 2015 at 8:26 PM, Jurgis Pralgauskis < jurgis.pralgauskis@gmail.com> wrote:

seems nice! :) few notices:

  • print("hi") doesn't seem to work.. probably would be good to have "debug" output alongside "standart output".. Output pane could be split in two parts..?
  • if the input is invisible, no variable is watched. If I toggle textbox to invisible, variables are still watchet/updated..

On Sun, Dec 6, 2015 at 4:52 PM, André Roberge notifications@github.com wrote:

I have changed the UI for this in two ways: adding placeholder text in the input which, hopefully should be nearly self-explanatory; using only one image/button which is used to toggle the input: if the input is invisible, no variable is watched. The previous "red magnifying glass" could be confusing: does pressing the red button means stop ... or does it mean that something is stopped?

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162322034.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

aroberge commented 8 years ago

Fixed broken print. I was trying to be too smart, appending the output of the watch to the previous recorded frame so that they would appear to be kept in sync. However, because both the watch() and the print() used a code of "output" for the frame, the watch appended would effectively replace the print.

I need to fix the toggle so that it works properly.

I'll look at your smart filtering option some time later today.

aroberge commented 8 years ago

Thinking about it ... In some ways, I was trying too hard to re-use existing dialog and code. I think I need to create a few more dialogs, and separate out the logic of various parts so that they cannot interfere with each other. I like to give them somewhat descriptive titles, rather than just "debug". Including the current ones, I would have:

  1. Reeborg writes (blue gradient top bar), showing the output from print
  2. Reeborg says (green gradient top bar): for positive end of program
  3. Reeborg shouts (flat red top bar): any error condition, or goal not met
  4. Reeborg watches (color?): for watching variables
  5. Reeborg explores (color?): for view_source_js ... which is inexplicably not working anymore
  6. Reeborg writes fancily (color?): for print_html()

Currently, 1, 4, 5 and 6 were all using the same dialog.

dz0 commented 8 years ago

I'll look at your smart filtering option some time later today.

Ok, no hurry :)

generally manual input is much flexibler -- as you can watch expressions!

Though one should be carefull if those expressions change the state of world/variables if they use functions .. But I guess nobody would need to watch "move()"

On Sun, Dec 6, 2015 at 8:52 PM, André Roberge notifications@github.com wrote:

Fixed broken print. I was trying to be too smart, appending the output of the watch to the previous recorded frame so that they would appear to be kept in sync. However, because both the watch() and the print() used a code of "output" for the frame, the watch appended would effectively replace the print.

I need to fix the toggle so that it works properly.

I'll look at your smart filtering option some time later today.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162336663.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

dz0 commented 8 years ago

Would be good to track when the watch value changes -- for students to notice it better :)

I tried to patch your code (but haven't tested):

previuos_watch_values = {}
def watch(args, loc={}):
    global  previuos_watch_values
    current_watch_values = {}  # collect current values here

    out = []
    f = "<span class='watch_name'>{}</span>: <span class='watch_value'>{}</span>"  # NOQA
    changed = "<span class='changed' style='color:red'>{}</span>"
    for arg in args:
        try:
            value = eval(arg, globals(), loc)
            current_watch_values[ arg ] = value

            out.append(f.format(arg, value))

            # if value changed, highlight it (Visual Studio does it in red)
            if not arg in previuos_watch_values \
               or value != previuos_watch_values[arg] :
                out[-1] = changed.format( out[-1] )  

        except:
            pass
    window.print_html("<br>".join(out))
    previuos_watch_values = current_watch_values         # forgets currently unused args

ps.: maybe "watch" should be renamed to "_watch" just in case ..

dz0 commented 8 years ago

Reeborg watches (color?): for watching variables

or maybe "Inspects"? and maybe its not Reeborg who watches -- its coder? I like Orange :)

Reeborg explores (color?): for view_source_js ... which is inexplicably not working anymore

Brown (because view'ing code is solid, and brown seems solid for me :)

Reeborg writes fancily (color?): for print_html()

Magenta (because it's fancy :)

On Sun, Dec 6, 2015 at 9:14 PM, André Roberge notifications@github.com wrote:

Thinking about it ... In some ways, I was trying too hard to re-use existing dialog and code. I think I need to create a few more dialogs, and separate out the logic of various parts so that they cannot interfere with each other. I like to give them somewhat descriptive titles, rather than just "debug". Including the current ones, I would have:

  1. Reeborg writes (blue gradient top bar), showing the output from print
  2. Reeborg says (green gradient top bar): for positive end of program
  3. Reeborg shouts (flat red top bar): any error condition, or goal not met
  4. Reeborg watches (color?): for watching variables
  5. Reeborg explores (color?): for view_source_js ... which is inexplicably not working anymore
  6. Reeborg writes fancily (color?): for print_html()

Currently, 1, 4, 5 and 6 were all using the same dialog.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162337768.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

aroberge commented 8 years ago

Watch variable has now a complete implementation that should work properly ... It takes a bit longer to run a program (i.e. to compute each frame before being ready to display). It prompted me to add a __str__ method to the UsedRobot class. If you create a new robot, you will see the result.

dz0 commented 8 years ago

Yahoo! :)

Someday it could integrate PythonTutor, if it can generate its trace format ;) https://github.com/pgbovine/OnlinePythonTutor/blob/master/v3/docs/opt-trace-format.md

On Mon, Dec 7, 2015 at 6:40 AM, André Roberge notifications@github.com wrote:

Watch variable has now a complete implementation that should work properly ... It takes a bit longer to run a program (i.e. to compute each frame before being ready to display). It prompted me to add a str method to the UsedRobot class. If you create a new robot, you will see the result.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162411204.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

dz0 commented 8 years ago

str method to the UsedRobot class

it should probably also include orientation for complete info? and tokens?

On Mon, Dec 7, 2015 at 6:40 AM, André Roberge notifications@github.com wrote:

Watch variable has now a complete implementation that should work properly ... It takes a bit longer to run a program (i.e. to compute each frame before being ready to display). It prompted me to add a str method to the UsedRobot class. If you create a new robot, you will see the result.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162411204.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

dz0 commented 8 years ago

for watch not to have delay, we probably can use

locals()[arg]

instead of eval?

eval would be usefull for expressions, for example when using lists with indexes: A[i] so textbox could be left with hint: expr1, expr2

but this can be left for next weekend :)

dz0 commented 8 years ago

if I use some name, which is reserved, it is not shown in watch-list

ex.: vars = [10, 5]

On Mon, Dec 7, 2015 at 8:45 AM, Jurgis Pralgauskis < jurgis.pralgauskis@gmail.com> wrote:

for watch not to have delay, we probably can use

locals()[arg]

instead of eval?

eval would be usefull for expressions (for example when using lists with indexes A[i])

On Mon, Dec 7, 2015 at 8:30 AM, Jurgis Pralgauskis < jurgis.pralgauskis@gmail.com> wrote:

str method to the UsedRobot class

it should probably also include orientation for complete info? and tokens?

On Mon, Dec 7, 2015 at 6:40 AM, André Roberge notifications@github.com wrote:

Watch variable has now a complete implementation that should work properly ... It takes a bit longer to run a program (i.e. to compute each frame before being ready to display). It prompted me to add a str method to the UsedRobot class. If you create a new robot, you will see the result.

— Reply to this email directly or view it on GitHub https://github.com/aroberge/reeborg/issues/196#issuecomment-162411204.

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

Jurgis Pralgauskis tel: 8-616 77613; Don't worry, be happy and make things better ;) http://galvosukykla.lt

aroberge commented 8 years ago

The slowdown was likely cause the the filtering function; I removed it and it is faster. Direct lookup instead of using eval also seems to make it faster; thanks for the suggestion.

Handling of variable assignments for builtins, like vars now work.

Regarding P. Guo's Python tutor: my understanding is that it extracts information based on CPython's internals, which are unlikely to be equivalent in Brython. I think that what I have now is "good enough".

I do have one thing that puzzles me: with the nonlocal scope, we could have a third scope. However, there does not exists an equivalent to locals() or globals() for nonlocal as far as I can tell. I think I'll ask on the Brython list to see if it is possible to write an equivalent function.

@dz0 Waiting for your feedback before I close this issue ... hopefully for good this time!

dz0 commented 8 years ago

Looks nice! :)

Someday i would like to be able to watch expressions (which would need eval way), but in most educational cases current var list is enough, and one can just print expressions... Less is more :)

Python tutor might consider Brython on their own -- so they wouldn't need serverside. Maybe I'll propose this to them :)

Thanks for now

aroberge commented 8 years ago

Try the following on world Center 1.

add_watch("dict(default_robot().objects)")
add_watch("default_robot().x")
move()
move()
put()
move()

btw, it should now work with Firefox as well as with Chrome (and Opera, and Edge...)

dz0 commented 8 years ago

by the way, Brython nearly has watches as well...

https://github.com/amrdraz/python-debugger/issues/2#issuecomment-164898778