Thursday, March 23, 2017

PyDev 5.6.0 released: faster debugger, improved type inference for super and pytest fixtures

PyDev 5.6.0 is now already available for download (and is already bundled in LiClipse 3.5.0).

There are many improvements on this version!

The major one is that the PyDev.Debugger got some attention and should now be 60%-100% faster overall -- in all supported Python versions (and that's on top of the improvements done previously).

This improvement was a nice example of trading memory vs speed (the major change done was that the debugger now has 2 new caches, one for saving whether a frame should be skipped or not and another to save whether a given line in a traced frame should be skipped or not, which enables the debugger to make much less checks on those occasions).

Also, other fixes were done in the debugger. Namely:

  • the variables are now properly displayed when the interactive console is connected to a debug session;
  • it's possible to select the Qt version for which QThreads should be patched for the debugger to work with (in preferences > PyDev > Debug > Qt Threads);
  • fixed an issue where a native Qt signal is not callable message was raised when connecting a signal to QThread.started.
  • fixed issue displaying variable (Ctrl+Shift+D) when debugging.

Note: from this version onward, the debugger will now only support Python 2.6+ (I believe there should be very few Python 2.5 users -- Python 2.6 itself stopped being supported in 2013, so, I expect this change to affect almost no one -- if someone really needs to use an older version of Python, it's always possible to get an older version of the IDE/debugger too). Also, from now on, supported versions are actually properly tested on the ci (2.6, 2.7 and 3.5 in and 2.7, 3.5 in

The code-completion (Ctrl+Space) and find definition (F3) also had improvements and can now deal with the Python super (so, it's possible to get completions and go to the definition of a method declared in a superclass when using the super construct) and pytest fixtures (so, if you have a pytest fixture, you should now be able to have completions/go to its definition even if you don't add a docstring to the parameter saying its expected type).

Also, this release improved the support in third-party packages, so, coverage, pycodestyle (previously and autopep8 now use the latest version available. Also, PyLint was improved to use the same thread pool used in code-analysis and an issue in the Django shell was fixed when django >= 1.10.

And to finish, the preferences for running unit-tests can now be saved to the project or user settings (i.e.: preferences > PyDev > PyUnit > Save to ...) and an issue was fixed when coloring the matrix multiplication operator (which was wrongly recognized as a decorator).

Thank you very much to all the PyDev supporters and Patrons (, who help to keep PyDev moving forward and to JetBrains, which sponsored many of the improvements done in the PyDev.Debugger.

Tuesday, January 31, 2017

PyDev 5.5 released

The main features introduced in PyDev 5.5 are:

  • Ctrl+Shift+Alt+O allows jumping directly to the last hyperlink in the console (which means that when you have some exception on the console, it can be used to got directly to the error location without using a mouse).
  • Ctrl+2, sw switches the target and value in an assignment (but may not work properly if more than one '=' char is found in the line).
  • The code-completion which adds a local import can now be configured to add the local import to the top of the method, not only in the line above the current line (to use it, request a code-completion for some token which needs to be imported, then press tab to focus the completion pop-up and apply the completion with Shift pressed).
  • Another improvement in code-completion is that it now properly supports method chaining and provides 'value' and 'name' fields when accessing enums.

Apart from those, multiple bug-fixes are also available (in refactoring, hovering on debug, parsing nested async calls on Py3 and grouping imports).


p.s.: Thank you very much to all the PyDev supporters and Patrons (, who help to keep PyDev moving forward.

For LiClipse users, 3.4.0 is already available with the latest PyDev.

Wednesday, November 30, 2016

PyDev 5.4.0 (Python 3.6, Patreon crowdfunding)

PyDev 5.4.0 is now available.

The main new feature in this release is support for Python 3.6 -- it's still not 100% complete, but at least all the new syntax is already supported (so, for instance, syntax and code analysis is already done, even inside f-strings, although code-completion is still not available inside them).

Also, it's now possible to launch modules using the python '-m' flag (so, PyDev will resolve the module name from the file and will launch it using 'python -m'). Note that this must be enabled at 'Preferences > PyDev > Run'.

The last feature which I think is noteworthy is that the debugger can now show return values (note that this has a side-effect of making those variables live longer, so, if your program cares deeply about that, it's possible to disable it in Preferences > PyDev > Debug).

Now, for those that enjoy using PyDev, I've started a crowdfunding campaign at Patreon: so that PyDev users can help to keep it properly maintained... Really, without the PyDev supporters:, it wouldn't be possible to keep it going -- thanks a lot! Hope to see you at too ;)

Friday, November 25, 2016

Python Import the world Anti-pattern!

I've been recently beaten from that anti-pattern which seems to be way too common in Python.

Now, what exactly is that "import the world" anti-pattern?

This anti-pattern (which I just made up) is characterized by importing lots of other files in the top-level of your scope of your module or package or doing too much at import time.

Now, you may ask: why is it bad? Every code I see in the Python world seems to be structured like that...

It's pretty simple actually: in Python, everything is dynamic, so, when you import a module you're actually making the Python interpreter load that file and run the bytecode, which will in turn generate classes, methods (and do anything which is in the global scope of your module or class definition)..

-- sure, even worse would be at import time going on to connect to some database or do other nasty stuff you wouldn't be expecting by just importing a module -- or who knows, going on to register to another service just because you imported some module! Importing code should be mostly free of side effects, besides, you know, generating those classes, methods and putting the module on sys.modules.

Ok, ok, I deviated from the main topic: why is it so bad having all those imports at the top-level of your module?

The reason is simple: nobody wants a gazillion of dependencies just because they partially imported some module for some simple operation.

It's slow, so much that any command line application that passed the toy stage -- and are concerned about the user experience -- have to hack around it.. really, just ask the mercurial guys.

It wastes memory (why loading all those modules if they won't be used anyways).

It adds dependencies which wouldn't be needed in the first place (like, you have a library which needs a lapack implementation which needs some ancient incarnations to be compiled that I don't care about because I won't be using the functions that need it in the first place).

It makes testing just a part of your code much slower (i.e.: you'll load 500 modules just for a small unit-test which touches just a small portion of your code).

It makes testing with pytest-xdist much slower (because it'll import all the code in all of its slaves instead of loading just what would be needed for a given worker).

So, please, just don't.

Ok, but does that really happen in practice?

Well, let me show you the examples I have stumbled in the last few days:

1. conda: Conda is a super-nice command line application to manage virtual environments and get dependencies. But let's take a look under the hood:

The main thing you use in conda is the command line, so, let's say you want to play with the "conda_env.cli.main" module. How long and how much does a simple: "from conda_env.cli import main"?

Let's see:

>>> sys.path.append('C:\\Program Files\\Brainwy\\PyVmMonitor 1.0.1\\public_api')
>>> import pyvmmonitor
>>> pyvmmonitor.connect()
>>> len(sys.modules)
>>> 123
>>> @pyvmmonitor.profile_method
... def check():
...     from conda_env.cli import main
>>> check()
>>> import sys
>>> len(sys.modules)

And it generates the following call graph:

Now, wait, what just happened? Haven't you just imported a module? Yes... I have, and in turn it has taken 0.3 seconds, loaded its configuration file under the hood (and made up some kind of global state?), parsed yaml, and imported lots of other things in turn (which I wish never happened) -- and it'd be even worse if you did a "conda env" command because it imports lots of stuff, parses the arguments and then decides to call a new command line with subprocess with "conda-env" and goes on to do everything again (see

2. mock: Again, this is a pretty nice library (so much that it was added to the standard library on Python 3), but still, what do you expect from "import mock"?

Let's see:

>>> sys.path.append('C:\\Program Files\\Brainwy\\PyVmMonitor 1.0.1\\public_api')
>>> import pyvmmonitor
>>> pyvmmonitor.connect()
>>> len(sys.modules)
>>> 123
>>> @pyvmmonitor.profile_method
... def check():
...    import mock
>>> check()
>>> import sys
>>> len(sys.modules)

And it generates the following call graph:

Ok, now there are less deps, but the time is roughly the same. Why? Because to define its version, instead of doing:

__version__ = '2.0.0'

it did:

from pbr.version import VersionInfo

_v = VersionInfo('mock').semantic_version()
__version__ = _v.release_string()
version_info = _v.version_tuple()

And that went on to inspect lots of things system wide, including importing setuptools, which in turn parsed auxiliary files, etc... definitely not what I'd expect when importing some library which does mocks (really, setuptools is for setup time, not run time).

Now, how can this be solved?

Well, the most standard way is not putting the imports in the top-level scope. Just use local imports and try to keep your public API simple -- simple APIs are much better than complex APIs ;)

Some examples: if you have some library which wants to export some methods at some __init__ package, don't import the real implementations and put them in the namespace, just define the loads and dumps as methods in __init__ and make local imports which will do the real work loading the actual implementation as lazy imports inside those methods (or do import it, but then, make sure that the modules that contain the loads and dumps don't have global imports themselves).

Classes may be the more tricky case if you want them to be the bases for others to implement (because in this case you really need the class at the proper place for users of your API), so, here, you can explicitly import that class to be available in your __init__, but then, make sure that the scope which uses that class will import only what's needed to define that class, not to use it.

Please, don't try to use tricks such as for your own library... it just complicates the lives of everyone that wants to use it (and Python has the tools for you to work with that problem without having to resort to something which will change how Python imports behave globally) and don't try to load some global state behind the scenes (explicit is way better than implicit).

Maybe the ideal would be having Python itself do all imports lazily (but that's probably impossible right now) or have some support for a statement such as from xxx lazy_import yyy, so that you could just shove everything at your top-level, but until then, you can resort to using local imports -- note: you could still can create your own version of the lazy import which you could use to put things in the global scope, but as it's not standard, IDEs and refactoring tools may not always recognize it, so, I'd advise against it too given that local imports do work properly (although if you want to do some global registry of some kind, register as strings to be lazily loaded when needed instead of importing modules/classes to fill up your registry).

Really, this is not new: ;)

Thursday, November 03, 2016

PyDev 5.3.1 (bugfix release)

PyDev 5.3.1 is now out.

This is mostly a bugfix release.

It fixes a regression added on 5.3.0 where Ctrl+1 was not properly resolving symbols from dependent projects, code analysis on a particular case with PEP 448 (list unpacking), auto-indents async and await blocks, highlights async and await blocks and fixes an issue in the code completion using aliases.

It does have one new feature in which Ctrl+Shift+O (which fixes unresolved tokens by adding the needed imports) uses the improved sorting also used for the code-completion (so that tokens from the project appear before tokens from other projects, which in turn appear before tokens from the standard library). Also, the substring completion is now on by default (it may be turned off in the code-completion preferences).

Enjoy ;)

Friday, October 14, 2016

PyDev 5.3.0 (Code completion improvements, Validate code with Python 2 & 3 while typing, PyQt5 event loop in interactive console)

PyDev 5.3.0 is now available for download.

The major focus in this release was in the code completion, with a number of enhancements...

The main one is that it's now possible to request completions by matching a substring of the requested name, not only on the first chars (to make use of it, it has to be enabled in Preferences > PyDev > Editor > Code Completion > Match substrings on code completion).

Now, after this change was implemented, I noted that it was hard to use it by itself because as more completions are brought for a name it was hard to actually reach the one you wanted, so, it triggered to have even more improvements to the code completion, especially in the sorting, so, now completions in PyDev are re-sorted when typing so that based on the current name typed the matches that are exact matches or start with the requested token appear first, then as a second heuristic, the type of the completion is taken into account (i.e.: parameters appear before context-insensitive completions) and at last, where the completion was found plays a role, so that completions which are from the current project appear before referenced projects (which in turn appear before the standard library modules).

Also, the completions now take advantage of styling, so, the part of the completion which was matched is now shown in bold and the part of the completion which shows a qualifier for the completion is also properly styled.

All in all, I'm really enjoying using this new code completion mode, and will probably make it the default in the next release.

Besides this, the apply mode when holding Ctrl and Shift changed a bit... Ctrl on apply now always means only "replace current name with completion" and Shift (when focusing the completion pop-up) means "do local import" on completions with auto-import (it's important to note that the focus on the pop-up is important as Shift+Enter on the editor already has an important role as it cancels the completion and moves the cursor to a new line) .

Other niceties of this release include the possibility of specifying that a project should be checked with a Python 2 and a Python 3 grammar (customized in Project Properties > PyDev - Interpreter/Grammar > grammars for "additional syntax validation") and the interactive console now supports PyQt5 for interactively inspecting a widget while using the console (i.e.: it frees the event loop to process events in the interactive console as it already did for Gtk, PySide, PyQt4, etc). It may be activated with %matplotlib qt5 (when using IPython/matplotlib) or in Preferences > PyDev > Interactive Console > Enable GUI event loop integration > PyQt5.

Enjoy ;)

Thursday, August 18, 2016

PyDev 5.2.0 released (static type declarations, debugger fixes, IPython 5 support in interactive console)

PyDev 5.2.0 is now out (for LiClipse users, it's available in LiClipse 3.1.0).

This is the first release which provides support for PEP 484 (static type declarations), which means that if you are using a static type declaration on for a parameter or method return, PyDev should be able to provide you with code completion based on that information.

As a disclaimer, I personally don't use that feature -- although I resort to putting type information on docstrings sometimes -- Python for me is about duck-typing and being a consenting adult responsible user, and type declarations don't seem to have a fit there for me... anyways, for those that think it's a good feature to have a more statically typed, Java-like structure / type system in Python, now PyDev is able to give you completions based on that information ;)

This release also has many other goodies that are noteworthy:

The debugger had a long standing issue where the variables wouldn't appear sometimes (and the user would have to re-select the stack frame for the variables to appear). In the latest release, PyDev should always show the variables properly (even if getting some variable takes time in the debugger backend). Also, when stepping over it should properly highlight in the variables view which variables changed and keep their expanded state.

The auto-indentation now better follows PEP 8, meaning that:
  • Indenting directly after {, [, ( will add one indent level.
  • Indenting after another token in a line with a {, [, ( will indent to the {, [, ( level.

Note: it's possible to restore previous indent modes (which either always indented to the parenthesis level or always indented a single level) in the Preferences > PyDev > Editor > Typing.

Another good thing is that IPython 5 is now properly supported in the interactive console (IPython 5 did some backward-incompatible changes, so, PyDev code had to be adjusted accordingly). Also, some fixes were done in the interactive console to deal with multi-line statements in Jython and dealing with multiple statements in a single line.

Those were the major noteworthy things, but not all that was done. See: for more information on what else was done in this release.

And as always, thank you very much to the PyDev supporters which help on keeping the PyDev development going strong.