

Testing, debugging and profiling
--------------------------------

### Interactive test

The Brythons site, or its mirror available for download, include a console where you can test Python code

Please note that the namespace is not refreshed when you click on "run", you must reload the page for that

For debugging and testing Brython, a number of test scripts are grouped in the directory `tests` ; you can access them by clicking the link  "Test pages" in the console, then select the different tests and run them

### Debugging scripts

Whatever the debugging level, syntax errors are reported in the browser console (or at the place defined by `sys.stderr`)

For instance, the code

>    x = $a

generates the message

>    SyntaxError: unknown token [$]
>    module '__main__' line 1
>    x = $a
>        ^

By setting the debugging level to 1 in the call to function <code>brython(_debug\_mode_)</code>, the exceptions raised at runtime and not caught by an `except` also produce an error message, as close as possible to the one generated by Python3

This code :

>    x = [1,2]
>    x[3]

generates :

>    IndexError: list index out of range
>    module '__main__' line 2
>    x[3]

### Debugging Javascript Generated Python Code

> TL;DR if you want to use the browser builtin debugger to step through your python Javascript code write `__debugger__` in your code and open the developer tools.

This statement is equivalent to the Javascript `debugger` statement.

Modern browsers such as FireFox and Google Chrome have built in debuggers, these debuggers allow developers to step through the code stepping into function calls and out (you know like debuggers in IDEs)

It is possible to debug javascript code by placing breakpoints on the line numbers inside the script tab in the developer tools.

However Brython generated javascript is generated during runtime and thus does not appear in a file, fortunately, browsers have added a special keyword to the javascript language called `debugger` this statement manually inserts a breakpoint into the script so that in runtime if the developer tools are open it will halt execution and start a debugging session there.

We have added to the Brython interpreter the keyword `__debugger__` which will be translated by the tokenizer to `debugger` thus triggering the same process.

To try it out now head over to the editor, type `__debugger__` in your code, open the developer tools (in chrome right-click inspect element), then click run.

to learn more about the chrome developer tools visit their documentation or this short course by code school.


### Debugging Python Code

A simple time-travel step back and forth debugger is implemented [here](../../tests/debugger.html)

As of this writing it is not full featured and supports only line step.
You will find documentation on how each function in the debugger works (in case you want to build on it)

Currently only python language specific programs are supported.

The debugger does not fully support the input statements; only supporting input with a string literal for argument (more on this below).


####Brython_Debugger For Developers

The debugger provides 4 hooks (on_debugging_started, on_step_update, on_debugging_end, and on_debugging_error) which take a callback that you can decide to do whatever you want with.

The way the debugger works in record mode (default) when you run `start_debugger` is by parsing the python code into brython generated js and then injecting a trace function before each $line_info occurrence (which requires running brython in debug mode higher than 0).

Additional trace calls; are injected at the start of the code before any line, for pointing at the first line; after while loops and at the end of the program, for pointing at the correct lines when debugging in an editor.

Since the debugger is not run live but recorded the parser replaces each call to the brython input function with a trace of type input with the arguments that were meant to be passed to the input function (currently only support string literals).

After injecting trace is complete the debugger runs the code which then fires the trace calls while running.

Each line trace call gets a state object as parameter with the current top frame and line number and records it. Before doing so the previous state's next line number is updated with the current state's line number; as while stepping in the editor the next line not the current line is what gets highlighted.

If the line trace is of type afterwhile or eof then it's state is not recorded.

If an input trace is called then a line state trace of type input is added and the debugger halts code execution, starting the debugging session.

When the line trace of type input is stepped on the user is prompted for input based on Brython's defined input function, the result is recorded and the program gets re-executed.

If there was no input trace then the debugging session will start after the parsed code is executed normally.


This debugger is still under development and changes will occur to the API

The debugger is available in the global scope from the window object under Brython_Debugger.

For an example on how it works see [debugger](../../tests/debugger.html)

If you want to add additional trace points call the setTrace function provided by the API inside your own function (currently must be globally accessible)

The following is the debugger public API you can find more details description in the code at www/tests/debugger/main.js


**Brython_Debugger**.`start_debugger()`
> start the debugging session, takes code to debug as parameter as well as an optional boolean flag for whether to live debug or record. Currently live debug is not supported and debugging by default starts in record mode.  The `on_debugging_started` callback is called at the end of this step

**Brython_Debugger**.`stop_debugger()`
> function to call when you want to stop the debugging session `on_debugging_end` is called at this step

**Brython_Debugger**.`step_debugger()`
> This function when called steps forward one step in the recorded debugging session

**Brython_Debugger**.`step_back_debugger()`
> This function when called steps backward one step in the recorded debugging session

**Brython_Debugger**.`can_step(n)`
> check if you can step to the specified step

**Brython_Debugger**.`set_step(n)`
> seek to a specific step in the recorded debugging session take a number from 0 to the last step as parameter. If a number larger than the last step is entered nothing will happen

**Brython_Debugger**.`is_debugging()`
> return whether a debugging session is active

**Brython_Debugger**.`is_recorded()`
> returns whether this debugger is in recording mode

**Brython_Debugger**.`is_last_step()`
> returns whether the current step is the last step

**Brython_Debugger**.`is_first_step()`
> returns whether the current step is the first step

**Brython_Debugger**.`get_current_step()`
> return a number indicating the current step

**Brython_Debugger**.`get_current_frame()`
> returns the current frame/state (it should be state)

**Brython_Debugger**.`get_recorded_frames()`
> returns all recorded states

**Brython_Debugger**.`set_trace_limit(Number)`
> The maximum number of steps executed before the debugger halts, default 10000

**Brython_Debugger**.`set_trace(obj)`
> object should contain the data you want paced later to the set_trace function
 do not use event names already used by the debugger
 add a trace call, (which will be called on step update)

**Brython_Debugger**.`set_trace_call(string)`
> Change the name of the traceCall Function that is injected in the brython generated javascript, used to record state, the default is Brython_Debugger.set_trace. To change it you would still need to call this function, so be careful and generally you don't need to.

**Brython_Debugger**.`on_debugging_started(cb)`
> cb is called after debugging session has started

**Brython_Debugger**.`on_debugging_end(cb)`
> cb is called after debugging session has ended

**Brython_Debugger**.`on_debugging_error(cb)`
> cb is called after either a syntax or runtime error occurs

**Brython_Debugger**.`on_step_update(cb)`
> cb is called whenever a state is changed using setState

### Profiling scripts

To enable profiling one has to pass the "profile" option to the brython function:

> brython({'profile':1})

When the `profile` option is > 0 the compiler adds additional code to the generated
javascript which collects profiling information. To `profile` module provides access
to this information. It strives to provide an interface largely similar to the `profile`
module from the standard python distribution.

The notable difference is that it does not allow user-defined timers and does not
do any calibration. Methods which in the standard module save the data to a file
save a JSON-serialized version of the data to the browser's local storage instead.

#### Basic usage:

>       from profile import Profile
>
>       p = Profile()
>       p.enable()
>       do_something()
>       do_something_else()
>       p.create_stats()

Which will print out something like:

>           1 run in 0.249 seconds
>
>       Ordered by: standard name (averaged over 1 run)
>
>       ncalls  tottime  percall  cumtime  var percall  module.function:lineno
>        101/1    0.023    0.000    1.012        0.010               .fact:180

where each line corresponds to a function and the different columns correspond  to

    ncalls      is the total number number of times the function was called
                (if the function was called non-recursively, the second number
                behind the backslash indicates how many calls were top-level calls
                in the recursion)

    tottime     is the total time (in seconds) spent in the function not including subcalls

    percall     is the average time spent in the function per call, not including subcalls

    cumtime     is the total time spent in function including subcalls

    var percall is the average time spent in function per one non-recursive call

    standard name is the name of the function in the form module.function_name:line_number

Optionally one can also use the following form, taking advantage of running the code
several times and averaging it out:

>       from profile import Profile
>
>       p = Profile()
>       p.call(function_to_profile,200,arg1,arg2,kwarg1=v1)

which will print out something like:

>           200 runs in 0.249 seconds
>
>       Ordered by: standard name (averaged over 1 run)
>
>       ncalls  tottime  percall  cumtime  var percall  module.function:lineno
>        101/1    0.023    0.000    1.012        0.010  function_to_profile:16

Collected profile data can be saved to local storage for later use:

>        p.dump_stats('run1')

Profile data can also be read back:

>        data = Stats('run1')

And aggregated together

>        data.add(p.dump_stats())
>        print(data)
