Setting up a robust data science development environment takes time, and it’s a process that’s rarely ever finished. If you’re the type who likes to get the most out of your tools, you’ll likely enjoy tweaking, optimising, and layering your workspace with productivity enhancements. That might mean refining your Python setup to easily manage multiple language versions and dependencies, or expanding your text editor with plugins for linting, code suggestions, unit test execution, and CI/CD integration.
The only constant is that your environment is always evolving. I recently moved from vim
to nvim
and rewrote much of my VimL-based configuration in Lua—something I’d never touched before. If you’re experimenting, learning, and building in parallel, having a clean way to isolate and test changes becomes invaluable.
Managing tools without installing them
In most cases, I use Homebrew to manage system components on my machine, and it works well. But there are situations where installing something locally feels excessive—especially if I only need it temporarily.
Solution
Examples provided below work on the same basis, the code and commands are executed within disposable Docker containers. The process of needing to install software on local machine is completely removed from the system
Example: Python AST across versions
Suppose you’re working with a simple Python script using the ast
module, which allows you to parse and analyse Python code as an abstract syntax tree. This is commonly used in tools like linters or code formatters, but also in more advanced metaprogramming scenarios.
Here’s a minimal script that parses the assignment x = 42
and checks whether the literal 42
is represented using ast.Num
.
Intuitively, we might expect this to return True
—after all, 42
is a number, and ast.Num
seems appropriate. But that’s not always the case.
Now we run it across various Python versions using Docker.
|
|
I will store this script as /tmp/check_ast.py
. Using the docker one liner, I will execute the script in multiple version of Python.
|
|
|
|
You’ll notice the results vary. Some versions return True
, others return False
. This reflects the evolution of the ast
module. Although ast.Constant
was introduced in Python 3.8 to unify literals like numbers, strings, and constants under one node type, ast.Num
, ast.Str
, and related nodes were not immediately retired. In fact, ast.parse()
continued to return ast.Num
in Python 3.8, 3.9, and even 3.10.0, to preserve compatibility.
As a result, isinstance(..., ast.Num)
still returned True
in those versions. The complete shift to ast.Constant
occurred in Python 3.11, where ast.parse()
finally stopped emitting the older nodes. This is a good example of how language-level changes may be rolled out gradually, and why it’s useful to test behaviour directly—rather than rely on changelog summaries alone.
Other interesting uses
For quick evaluation it is possible to direcltly jump into ipython console. I find this partilculary useful if I want to check running some code interactively in a specific version of Python.
|
|
The other trick that I find useful in those scenarios is to install packages while in Python interactive session by calling subprocess
, this can be easily achieved via running subprocess
command pointing to pip
as shown below:
|
|
Summary
One-line Docker commands are a lightweight, repeatable, and isolated way to test and explore code across environments—without cluttering your system. They’re particularly useful when comparing behaviour across language versions or running quick experiments in tools you don’t use daily.