Sunday, August 02, 2020

PyDev 7.7.0 released (mypy integration improvements, namespace packages)

This release brings multiple improvements for dealing with type hints as well as improvements in the Mypy integration in PyDev:

The MYPYPATH can now be set automatically to the source folders set on PyDev and the --follow-imports flag is set to silent by default (this flag is required because only one file is analyzed at a time in PyDev as failing to do so would end up showing errors for other files).



Personally, I think that with Python 3.8 it's finally possible to actually use Python type hints properly (due to the typing.Protocol -- a.k.a Duck typing -- support), so, more improvements are expected in that front on PyDev itself.

This release also brings many other improvements, such as support for pip-installed namespace packages, debugger fixes, support for parsing with the latest version of Cython and support for the latest PyTest.

There are actually many other minor improvements and bug-fixes in this release too. Check the release notes at http://www.pydev.org/ for more details!

Acknowledgements

Thanks to Luis Cabral, who is now helping in the project for doing many of those improvements (and to the Patrons at https://www.patreon.com/fabioz which enabled it to happen).

Thanks for Microsoft for sponsoring the debugger improvements, which are also available in Python in Visual Studio and the Python Extension for Visual Studio Code.

Enjoy!

--
Fabio

Thursday, June 11, 2020

PyDev 7.6.0 (Python 3.8 parsing fixes and debugger improvements)

PyDev 7.6.0 is now available for download.

This release brings multiple fixes to parsing the Python 3.8 grammar (in particular, dealing with f-strings and iterable unpacking had some corner cases that weren't well supported).

Also, the debugger had a number of improvements, such as:
  • Grouping variables which were previously hidden -- note that they can be hidden/shown when debugging from the variables view dropdown menu as shown in the screen below:

  • Better support for PySide2;
  • Show the disassembly of frames when sources aren't available:



Besides those, there were a number of other improvements... some noteworthy are support for the latest PyTest, faster PyDev Package Explorer, type inference for type comments with self attributes (i.e.: #: :type self.var: MyClass) and properly recognizing trailing commas on automatic import.

Acknowledgements

Thanks to Luis Cabral, who is now helping in the project for doing many of those improvements (and to the Patrons at https://www.patreon.com/fabioz which enabled it to happen).

Thanks for Microsoft for sponsoring the debugger improvements, which are also available in Python in Visual Studio and the Python Extension for Visual Studio Code.

Enjoy!
--
Fabio

Wednesday, March 18, 2020

How is frame evaluation used in pydevd?

First some background in frame evaluation:

Since Python 3.6, CPython has a mechanism which allows clients to override how it evaluates frames. This is done by changing PyThreadState.interp.eval_frame to a different C-function (the default being _PyEval_EvalFrameDefault). See: pydevd_frame_evaluator.pyx#L370 in pydevd (note that Cython is used there).

Note that this affects the Python runtime globally, whereas the regular Python tracing function -- set through sys.settrace() -- affects only the current thread (so, some of the caches for frame evaluation in pydevd are thread-local due to that).

How is this used in the debugger?

Well, the debugger doesn't really want to change how Python code is executed, but, there's another interesting side effect of the frame evaluation: it's possible to change the bytecode of the frame right before it's evaluated and CPython will interpret that bytecode instead of the original bytecode of the frame.

So, this works the following way: the frame evaluation function receives a PyFrameObject*, and at that point, the debugger checks the frame for existing breakpoints, if it has a breakpoint, it'll create a new code object which has a programmatic breakpoint (pydevd_frame_evaluator.pyx#L234) and change PyFrameObject.f_code to point to the new code object (pydevd_frame_evaluator.pyx#L358) -- when it reaches the programmatic breakpoint (pydevd_frame_tracing.py#L34), the regular (trace-based) debugger will kick in at that frame. Until that breakpoint is reached, frames are executed at full speed.

But if it runs at full speed, why is my program still running slower when using pydevd with frame evaluation?

Well, frames are executed at full speed, but, the debugger still adds some overhead at function calls (when it decides whether to add the programmatic breakpoint) and it also needs to add an almost no-op trace (pydevd_frame_evaluator.pyx#L95) function to sys.settrace -- which makes function calls slower too (this is needed because otherwise the debugger is not able to switch to the regular tracing by just changing the frame.f_trace as frame.f_trace is only checked when a tracing function is set for some thread through sys.settrace()). There are also some cases where it can't completely skip tracing for a frame even if it doesn't have a breakpoint (for instance, when it needs to break on caught exceptions or if it's stepping in the debugger).

It's interesting to note that even the regular (tracing) debugger on pydevd can run frames at full speed (it evaluates all frames and if a frame doesn't have a breakpoint the tracing for that frame will be skipped), the difference is that if a frame does have a breakpoint, that frame can run at full speed until it reaches the breakpoint in the frame eval mode, whereas in the regular mode each new line tracing event would need to be individually checked for a breakpoint.

If it just changes the bytecode, why use frame eval at all, can't you just change the bytecode of objects at a custom import hook? (which could have the benefit of avoiding the performance penalty of checking the frame on each new frame call)

There are 2 main reasons for that: the 1st is that breakpoints can change and when they change the frame evaluation would need to be completely shut down and only the tracing debugger would be valid from that point onwards (whereas right now, if breakpoints change, the tracing debugger kicks in for all the frames that were currently running but the frame evaluation debugger can still be used for new frames). The 2nd is that it can be hard to consistently do that if not just before frame evaluation (because user code can also change a method code and there are a number of corner cases to change the bytecode for live objects -- think of a function inside a function or decorated functions).

Note that this means that the debugger could probably get away with something simpler than frame evaluation and could potentially be applicable to other Python implementations (say, a different callback just before the frame is evaluated which allows to change the frame code... unfortunately it can't currently be done through the "call" event received by the trace function set by sys.settrace because at that point the frame is already being evaluated with the current code and at that point, even if it's changed, Python won't pick up that change).

That's it, hope you enjoyed pydevd using frame evaluation for debugging purposes 101 ;)





Friday, January 10, 2020

PyDev 7.5.0 Released (Python 3.8 and Cython)

PyDev 7.5.0 is now available for download.

The major changes in this release are Python 3.8 support and improved Cython parsing.

Python 3.8 should've been in 7.4.0 (but because of an oversight on my part during the build it wasn't, so, this release fixes that).

As for the Cyhon AST, Cython is now parsed using Cython itself (so, it needs to be installed and available in the default interpreter for PyDev to be able to parse it). The major issue right now is that the parser is not fault tolerant (this means that for code-completion and code-analysis to kick in the code needs to be syntax-correct, which is a problem when completing for instance variables right after a dot).

Fixing that in Cython seems to be trivial (https://github.com/cython/cython/issues/3303), but I'm still waiting for a signal that it's ok to add that support to make Cython parsing fault-tolerant.

Enjoy!

Wednesday, March 27, 2019

PyDev 7.2.0 released

PyDev 7.2.0 is now available for download.

This version brings some improvements to the debugger and a fix for when PyDev could not properly find pipenv which could impact some users.

See: http://pydev.org for more details.