The Python Experience

It took me a while to sit down and write this post, because I was actively experimenting with the VS Code setup for Python. I used it before, naturally, but having to write about something forces me to do my homework and make sure there are no obvious flaws in the suggestions I propose. Thus, I had to double-check all the tools and extensions I use for Python, and confirm that my setup is current.

In general, I have to admit that Visual Studio Code is not yet "there" in terms of Python support. I don’t have any specific expectations, but much of the advertised functionality does not really work in many cases. For example, it is possible to use "Rename Symbol" function to rename a class or a variable, and it will update all the references as expected. However, it is impossible to rename a file in the same manner, so if I need to rename a module, I’ll have to update all its imports manually. Autocomplete functions are hit or miss. Sometimes they work, sometimes don’t. Same with auto-import: sometimes I type some name from the standard library, and the system suggests importing the corresponding module, and sometimes it doesn’t suggest anything, even if it is something as simple as os.path.join() or json.loads(). I also had a very unpleasant experience with Pygame Zero. This library defines several global objects that Visual Studio Code cannot recognize. Thus, there is no autocomplete, and if I turn on a linting tool, it marks every such object as "unrecognized".

There are other annoyances here and there, so maybe I’ll have to give a second look at a real Python IDE like PyCharm (I tried Python in Visual Studio, and it turned to be not much better than VS Code at a glance). However, I managed to deal with most of them, and feel ready to share some experiences.

Projects, Folders, Environments

Probably, the most basic fact one needs to know is the difference between "opening a file" and "opening a folder". Visual Studio Code is a "text editor", which means that it (in theory) works with text files. However, it is also possible to open a folder rather than a file. What’s the difference? VS Code uses a folder as a substitute for a "project", so if I open a folder rather than a file, the system would understand that all the files there should be somehow handled together. For example, if I rename a symbol, VS Code would search for occurrences of the same symbol in other project files. It won’t happen if I open a file rather than a folder. In addition, VS Code stores certain project settings inside the .vscode subfolder. Some files there are device-independent and thus can be placed under version control.

As I mentioned previously, we have to recognize that developing a Python project without a virtual environment is a very bad idea. Thus, I presume that project-specific environments do exist, and we should be able to associate them with specific projects in Visual Studio Code. It isn’t hard to do, but surprisingly VS Code gives no options for any automation of this process (well, environments should be guessed according to the documentation, but I couldn’t manage to make it work). There is an "auto-selection" plugin called Python Auto Venv available, but it uses VS Code’s deprecated functionality, so I will probably need to find an alternative in the nearest future.

Pylint and Black

VS Code supports natively several basic code editing functions, such as refactoring and autocomplete. Code analysis and code formatting capabilities are also available, but they require certain third-party tools to be installed in the current virtual environment. In particular, it is possible to run Black every time a file is saved. Similarly, VS Code is able to check code conformance to the standard style guidelines with external tools like Pylint. I activated this option, and I am fine with most of Pylint’s recommendations so far. By default it disliked single-letter variable names (which is weird, as I don’t see anything wrong with x, y or i), but fortunately this behavior is adjustable.

I also enabled a plugin pylint-django, which makes Pylint Django-aware, and suppresses many false alarms. Unfortunately, the current version of this plugin crashes for no reason unless patched. Hopefully, the author will fix it sooner or later, but what really worries me is the way VS Code handles the crash. It simply doesn’t: crashes are silently ignored, so there is no way to tell whether Pylint found no errors or crashed. This is, unfortunately, a typical situation: there are many great features in theory, but every feature comes with some "but", making its usage annoying.

Note also the differences between "extensions" and "third-party tools". An extension is something enabled in VS Code globally, while a Python-based third-party tool has to be found in the active virtual environment. It means that I have to install Black, Pylint-django, etc. for every environment I use. This isn’t the end of the world as I anyway install dependencies with Poetry, but is, you know, annoying.

Tests and Code Complexity Metrics

Strict line length limits imposed by Black made me think about other places where my habits might disagree a bit with some common recommendations. One great extension I found is Codalyze. It provides a quick code complexity report, which is now hooked on one of Shortcut Menu Bar's buttons in my setup. I think the most useful information is "cyclomatic complexity report", which shows McCabe’s complexity score for each function. Usually I keep my functions well under 10 (which is the default threshold value in Codalyze), but sometimes I have to deal with code with so many nested if and for blocks, that I can’t even imagine how people managed to engineer it at all.

These days I experiment a lot with TDD. I tried switching into TDD mode many times before, but it seems to be as easy as quitting smoking every time. Probably, I will write about my ongoing experiences later, but now it suffices to say that my current Python setup also includes the support for unit testing.

It seems that the pytest framework is almost universally preferred over the built-in unittest module of Python nowadays, so I decided to try it out. First, pytest has to be installed into the current virtual environment just like Pylint and Black, and then configuring VS Code is easy. In addition to plain pytest I also use its Django plugin. In general, VS Code provides quite a thin layer over pytest, so there is not much to discuss here. Test discovery and execution are pytest capabilities, so I can only confirm they work fine from the editor UI.

Are there any annoyances related to the pytest use? Well, you bet it. When a certain test fails, VS Code jumps to the beginning of a failed function and shows a very weird window that combines the function declaration line along with the pytest printout.

pytest output

I find this report very confusing. It’s hard to understand which assertion has failed, and it’s impossible to edit the code quickly. So I have to close the report first, and then find the failing line myself. In addition to this, "Run Tests" button does not save open files. So even if I fixed the code, I still have to save it in order to see whether it helped. I don’t see neither logic nor consistency here. Running a file, for example, causes it to be saved. Frankly speaking, when I started writing this post, I had no intention to turn it into a rant, but I just can’t overlook these things.

Another tool I found useful for testing is pytest-cov, which builds coverage reports for unit tests. Pytest-cov is a version of coverage, specifically tailored for the use with pytest. As far as I can tell, VS Code doesn’t have any special means for viewing coverage reports, so I won’t go into further details here.

Conclusion

Visual Studio Code is hard to review, because it isn’t really clear whether we should treat it as a "powerful text editor" or a "watered-down IDE". As a text editor it shines, providing a lot of capabilities in addition to generally expected functionality. However, it leaves a lot of mixed feelings as an IDE. There are many things that work "almost right", and, frankly speaking, I doubt they will be improved anytime soon. Let’s say, in general I am not overly annoyed, and I am ready to continue using VS Code. However, I am keen to dig a bit further and understand what is the real "state of the art" in this field in 2021. It’s okay to use a slightly rough tool, but it’s good to keep your expectations realistic and to understand what are the alternatives.