I use the SAS Extension for Visual Studio Code for projects that also involve a mix of Python, with Python projects containing different configuration, version specifications, and package dependencies. I find installing and maintaining those artifacts cumbersome and error-prone.
Over time, I have developed practices that suit my working style and yield organisation, isolation and automation benefits. I execute them through a shell script which I call my “build” script. Here’s an outline.
I have been using a Mac laptop for a long time and store several projects under a central folder called current_projects. That’s a bit of a misnomer because I am not very meticulous about archiving my folders so you find everything there happens to be “current”. Within each folder is a buildfolder. The purpose of this build folder is to:
.env file.
The idea is to implement a “boring consistency”. I expect to find a buildfolder in every project folder I open and can trigger the same so that I can develop with the most relevant configuration.
current_projects
- project1
- build
- build.sh
- requirements.txt
- project2
- build
- build.sh
- requirements.txt
- projectN
- build
- build.sh
- requirements.txt
Projects utilise several distinct packages, some of which require a specific Python version. Attempting to install and maintain all packages in my base Python environment is foolhardy and will likely result in version and dependency conflicts. Virtual environments are useful in such situations because they help me package my solution specifications in a portable, reusable and repeatable manner. My typical build.sh contains the following.
#!/usr/bin/env bash
set -euo pipefail
# Create and activate virtual environment
python -m venv buildproj
. buildproj/bin/activate
# Upgrade pip
python -m pip install --upgrade pip
# install uv, a fast Python package manager
pip install --upgrade uv
# Use uv to install from requirements.txt
uv pip install -r requirements.txt --force-reinstall --upgrade
# I find it easy to work with Jupyter notebooks and so attach my venv kernel to ipykernel to surface the same in Jupyter
python -m ipykernel install --user --name=buildproj
# If I want to run some automated scripts after installing the virtual env, now is the opportunity.
# python scripts/
# I activate the environment upon need so deactivate the venv for now.
deactivate
# I keep the below commented during development and use only for test runs or for cleanup after an automated script.
# rm -rf buildproj
Some points to note from the above script.
uv as a Python package manager due to its faster speed compared to the standard pip utility.python scripts/ section) and then remove the folder altogether. Saves some space.jupyter-lab . To ensure my notebook accesses the right Python kernel (i.e. the one associated with the virtual environment), I use ipykernel to install a kernel pointing to the venv I created.
This way, irrespective of where I choose to develop, my configuration travels with my code. I can push my project to a GitHub repository, clone it from another machine (assuming I have Python of the same version installed) and run the build.sh script to create my virtual environment afresh.
While convenient, I’d like it even better were I not be required to remember to run this script every time I open a project. Visual Studio Code’s automation capabilities, offered through tasks, come in useful. Here’s what I need to consider when opening a project.
__pycache__ and .ipynb_checkpoints from my previous development session need to be cleaned up.requirements.txtbuild script
These folders aren’t trivial and, depending on the size and number of projects, take up space on my workstation. I’d prefer to not even have my projects sit in my workstation anyways, isn’t that what GitHub is for?
Tasks help you automate routine activities in Visual Studio Code. Tasks can be folder-specific or workspace-specific and can call additional scripts upon certain triggers (such as opening a folder for instance). Tasks can also be sequenced.
Create a Python program which resides in a scripts folder in your project (you may choose to even have this reside in build but I like to keep them separate). An example is shown here.
from pathlib import Path
import shutil
root = Path(__file__).resolve().parents[1]
delete_dirs = {"__pycache__", ".ipynb_checkpoints", ".pytest_cache", "buildproj", "node_modules"}
delete_files = {".DS_Store"}
for path in root.rglob("*"):
if path.is_dir() and path.name in delete_dirs:
shutil.rmtree(path, ignore_errors=True)
elif path.is_file():
if path.name in delete_files or path.suffix == ".pyc":
try:
path.unlink()
except FileNotFoundError:
pass
This program searches for specified directories and files anywhere within the repository and deletes them. I have included common files which I do not require (since I shall probably be recreating them as I develop).
Next, create a folder called .vscode (it may already exist) in the root folder of your project and add a tasks.json there. If a tasks.json exists, it’s possible that you already know how to define a task and in this case, would be adding one more. Here’s the block to add to your tasks.json.
{
"version": "2.0.0",
"tasks": [
{
"label": "Clean Python cache",
"type": "shell",
"command": "python",
"args": [
"${workspaceFolder}/scripts/clean_cache.py"
],
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "never",
"panel": "shared"
},
"problemMatcher": []
}
]
}
This is only the first task. Simply expressed, it runs every time this folder ( ${workspaceFolder} ) is opened and executes a Python program (the one above) that cleans up unwanted folders and files. Tasks can also be configured through the command palette of Visual Studio Code.
The second task, which you would ideally want to have run after the first task is as follows. This may also be added to the tasks list in the tasks.jsonfile above.
{
"label": "build_environment",
"type": "shell",
"command": "cd build && ./build.sh",
"args": [
],
"dependsOn": "Clean Python cache",
"dependsOrder": "sequence",
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": []
}
Once you’ve deleted unwanted files and folders, you can proceed with building the environment afresh. Note the additional options here: dependsOn and dependsOrder . dependsOn specifies that the build_environmenttask depends on the successful run of the Clean Python cache task. dependsOrder specifies that this should run in sequence, i.e. after Clean Python cache. The final tasks.json is as follows.
{
"version": "2.0.0",
"tasks": [
{
"label": "Clean Python cache",
"type": "shell",
"command": "python",
"args": [
"${workspaceFolder}/scripts/clean_cache.py"
],
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "never",
"panel": "shared"
},
"problemMatcher": []
},
{
"label": "build_environment",
"type": "shell",
"command": "cd build && ./build.sh",
"args": [
],
"dependsOn": "Clean Python cache",
"dependsOrder": "sequence",
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": []
}
]
}
Test this by closing (File -> Close Folder) and then reopening the project folder. Be prepared to show a little patience the first time as waiting for the task to complete may take some getting used to.
Be aware of the tradeoffs. Depending on their complexity, tasks may run for some time and you may need to wait a short while after opening the folder to commence your work. Also, some projects with complex dependencies and heavy packages might be better off left alone without having to rebuild them every time. Your configuration is a carefully crafted context-contingent choice.
Creating a build script enables rapid experimentation and development and provides you repeatability and portability benefits. For a starter script, refer this GitHub repository and feel free to provide your suggestions in the comments section.
GitHub repository: https://github.com/SundareshSankaran/build-script
Nearly 200 sessions are now available on demand with the SAS Innovate Digital Pass.
Explore Now →The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.