Skip to content

Instantly share code, notes, and snippets.

@Voronoff
Last active September 29, 2023 16:41
Show Gist options
  • Save Voronoff/059c50f9fd354386c305c55af1f3a61f to your computer and use it in GitHub Desktop.
Save Voronoff/059c50f9fd354386c305c55af1f3a61f to your computer and use it in GitHub Desktop.
Windows development in 2018: Setting up a coding environment using Windows Subsystems for Linux (WSL), Hyper, and Visual Studio Code (vscode) with Python.

If you're here just for the section on vscode working with Python on WSL, jump here.

Creating a modern development environment in Windows

Windows is now a development environment that can compete with Mac and Linux. Windows Subsystems for Linux lets you have an Ubuntu (or other Linux flavor) installation that works near seemlessly inside of Windows. Hyper Terminal running WSL's Bash and Visual Studio Code feel really nice to code in. These are instructions for getting set up and smoothing out most of the remaining rough edges. I've included a section on getting vscode to work well with Python and WSL, but the general pattern should be usable for any unsupported language (as of now, I believe it's only Node.js that has WSL support in vscode).

Table of Contents


Install WSL

Requirement: The Windows user can't have spaces in the name. vscode extensions can have issues with WSL if there is a space in the Windows user name.

  • Follow the instructions here

Install Visual Studio Code

  • Download the default 64 bit vscode from here and install it.

Install and set up Hyper Terminal

Hyper is the terminal application for Windows that lives up to modern standards. It's built on Node and is rebindable, extendable, and otherwise customizable. However, WSL has a few extremely annoying defaults that affect all terminals, so we'll to fix those before doing anything else.

  • Download Node.js for Windows here and install
  • Download Hyper.js for Windows here and install

Set Hyper to use WSL's Bash by default

  • Open up Hyper and type Ctrl + ,
  • Scroll down to shell and change it to C:\\Windows\\System32\\bash.exe
  • Reopen Hyper

Get rid of directory background colors for ls

  • In Hyper, open your bash profile in your Linux user's home directory nano ~/.bashrc
  • Add this to your .bashrc (using Shift + Insert to paste):
# Change ls colours
LS_COLORS="ow=01;36;40" && export LS_COLORS

Add a shortcut to change the working directory to the Windows user's home folder

  • Add this to the same file, replacing [user name] with your Windows user name:
# For switching to mounted home easily
cd() {
    if [[ $@ == "m~" ]]; then
        command cd "/mnt/c/Users/[user name]"
    else
        command cd "$@"
    fi
}
  • Ctl + o to save and Ctl + x to exit
  • Reload your .bashrc using source ~/.bashrc

Disable the bell sound for the terminal

  • Create and open .inputrc in your Linux home directory using Nano: nano ~/.inputrc
  • Add this to .inputrc:
# Disable the bell sound on backspace
set bell-style none
  • Load the .inputrc using source ~/.inputrc

Update packages

  • In Hyper, update your list of packages sudo apt update
  • In Hyper, upgrade packages to their newest versions sudo apt upgrade

It's good to do this every week or two to stay up to date.

Install and set up a Sublime GUI for editing Linux files

Install MobaXterm

  • Download the Windows client from here, extract, and install
  • Start MobaXTerm
  • Go to Settings -> Configuration -> X11 and make sure Clipboard is set to: disable "copy on select"

Install Sublime

  • In Hyper terminal do:
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
sudo apt install apt-transport-https
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt update
sudo apt install libgtk2.0
sudo apt install sublime-text

Make Sublime's GUI accessible using MobaXterm

  • Open your .bashrc in nano again using nano ~/.bashrc
# Use MobaXterm to have a sublime GUI for editing linux files
export DISPLAY=:0

export PATH="$PATH:/mnt/c/Program Files (x86)/Mobatek/MobaXterm"
  • Reload your .bashrc with source ~/.bashrc

Make it easy to open MobaXterm from bash

  • Install x11-server-utils to get the xset command to see if the server is running sudo apt install x11-xserver-utils

  • Open your .bashrc in a GUI using sublime subl ~/.bashrc

  • If you want to have MobaXterm on Windows start whenever you open up a bash shell so that you always have the ability to open Sublime quickly add this to your .bashrc :

# Start MobaXterm on opening terminal if it's not running
if ! xset q &>/dev/null; then
    (
    command MobaXterm.exe -hideterm  &
    )&>/dev/null
fi
  • If you'd rather manually open MobaXterm when you want to use Sublime, this command will let you do both steps at once. It does take a bit of time for MobaXterm to start after you use it. Add it to your .bashrc :
# A command to open a file in sublime, and start MobaXterm first if it's not running
sublx () {
        if ! xset q &>/dev/null; then
            echo "Starting MobaXterm..."
            (
                command MobaXterm.exe -hideterm  &
                while ! xset q &>/dev/null; do
                    command sleep 1
            done
                command subl "$@" &
            )&>/dev/null
                else
            (
                command subl "$@" &
            )&>/dev/null
        fi
}

Both will only start MobaXterm if it isn't already running, and they'll both hide it after it starts up. You can access it directly by clicking on the ^ on your Windows task bar to see applications running in the background.

Install and set up Python to work with vscode and WSL

Note: if you already have a Python that you want to use instead of setting it up through pyenv, point to that Python bash.exe of your bat files.

Installing Python

  • Run the following to get the Linux tools for installing new Pythons:
sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev
  • Install Git sudo apt install git
  • Follow the instructions here to install Pyenv, Pipsi, and Pipenv for your Linux. Make sure to follow the instructions for Ubuntu and write to your .bashrc and not .bash_profile. You must resetart Hyper after installing Pyenv, and you should install the latest stable Python (currently Python 3.6.5).

VScode Python

The one problem remaining with this setup is that vscode and WSL don't play well together yet for anything besides Node. You can't select the version of a language that lives in your Linux installation for vscode running, debugging, linting, code completion, or navigating using workspace symbols. This is a known issue that both the general WSL team and various language plugin teams are aware of and working on.

In the mean time, one solution is to create parallel installations. Linux is the runtime environment, but vscode talks to a Windows install. However, that can cause issues with the debugging, linting, and code completion. To solve this, we can manually set up bat files that translates between the Windows and Linux paths for Python and tell vscode to use that.

  • Start vscode from Hyper code &
  • Open the list of extensions Ctl+Shift+x
  • Search for and install Python by Microsoft
  • Restart vscode
  • Go to your Windows user's home directory cd m~
  • Create a new directory for the config file mkdir .vscode_bats
  • Create and open python3.bat using vscode code .vscode_bats/python3.bat &
  • Add this to python3.bat to translate paths between Linux and Windows and point to the python being used by pyenv:
@echo off
set v_params=%*
set v_params=%v_params:\=/%
set v_params=%v_params:c:=/mnt/c%
set v_params=%v_params:"=\"%
bash.exe -c "~/.pyenv/shims/python %v_params%"
  • Install pylint pip install pylint
  • And create code .vscode_bats/pylint.bat &
@echo off
set v_params=%*
set v_params=%v_params:\=/%
set v_params=%v_params:c:=/mnt/c%
set v_params=%v_params:"=\"%
bash.exe -c "~/.pyenv/shims/pylint %v_params%"
  • Install exuberant ctags sudo apt install exuberant-ctags
  • And create code .vscode_bats/ctags.bat &
@echo off
set v_params=%*
set v_params=%v_params:\=/%
set v_params=%v_params:c:=/mnt/c%
set v_params=%v_params:"=\"%
bash.exe -c "ctags %v_params%"
  • Open vscode settings using Ctl+, within vscode
  • Add this between the {} in user settings on the righthand side, replacing [user name] with your Windows user name:
    "python.pythonPath": "C:\\Users\\[user name]\\vscode_bats\\python3.bat",
    "python.linting.pylintPath": "C:\\Users\\[user name]\\vscode_bats\\pylint.bat",
    "python.workspaceSymbols.ctagsPath": "c:\\Users\\[user name]\\vscode_bats\\ctags.bat"
  • By default linting happens on save, so if you want linting as you type, you can add the setting
    "files.autoSave": "afterDelay"

which defaults to

    "files.autoSaveDelay": 1000

Using Python Virtual Environments

We now have vscode talking to the Linux python enabled by pyenv, but we're using pipenv to manage Python and packages on a per project basis. To get vscode running with that, I created a bash function which creates and/or sets vscode workspace configuration to use the local pipenv virtual environment in the project folder.

  • Install jq sudo apt install jq
  • Install sponge sudo apt install moreutils

THIS REQUIRES HARD TABS, NOT SPACES. The EOF syntax doesn't like spaces for indentation.

  • Open .bashrc subl .bashrc
  • Add:
# Functions to create workspace settings for vscode that tell it to use the local Python virtual environment
code_setup() {
	program=$1
	cat > .vscode/$program.bat <<- EOF
	@echo off
	set v_params=%*
	set v_params=%v_params:\=/%
	set v_params=%v_params:c:=/mnt/c%
	set v_params=%v_params:"=\"%
	bash.exe -c "$venv_url/bin/$program %v_params%"
	EOF

	windows_path="$PWD/.vscode/$program.bat"
	windows_path=${windows_path/\/mnt\/c\/Users\//C:\/Users\/}
	windows_path=${windows_path//\//'\\'}
	if [ $program == "pylint" ]; then
		program="linting.pylint"
	fi
	if grep -qF "python.${program}Path" .vscode/settings.json; then
		if sed -i.bak "s+\"python.${program}Path\":.*(,)$+\"python.${program}Path\": \"${windows_path}$1\"+" .vscode/settings.json; then
			echo "vscode set to use local virtual environment for $program in wokspace settings"
			rm .vscode/settings.json.bak
		else
			echo "Changing workspace settings failed with error: $?"
			echo "Reverting to previous vscode workspace settings"
			mv -f .vscode/settings.json.bak .vscode/settings.json
		fi
	else
		echo "$(jq ". += {\"python.${program}Path\": \"$windows_path\"}" .vscode/settings.json)" | sponge .vscode/settings.json
		echo "adding python.${program}Path to vscode workspace settings"
	fi
}

code_pipenv() {
	venv_url=$(pipenv --venv)
	mkdir -p .vscode
	if ! [ -f .vscode/settings.json ]; then
		cat > .vscode/settings.json <<- EOF
		{
		}
		EOF
	fi
	code_setup python
	code_setup pylint
}
  • Reload your .bashrc using source ~/.bashrc
  • Navigate to the root folder of a python project that uses pipenv or create one:
    • cd m~
    • mkdir -p projects/python_vscode_test
    • cd projects/python_vscode_test
    • pipenv --python 3.6.5
    • pipenv install -d pylint
    • touch test.py
  • Run code_pipenv in Bash to create or modify vscode workspace settings to use the local virtual environment for Python and Pylint.
  • Open the folder in vscode vscode . & and use Ctl + , to check that workspace settings are set (righthand side, second tab)
  • Edit test.py with code completion, navigation, and linting.

code_pipenv only needs to be run once per project, unless the project is set to use a different virtual environment. In that case, just run it again.


And you're off to the races! Hyper and vscode have a ton of cool features and extensions to explore.

If you use any CSV data, the vscode extensions Excel Viewer and Rainbow CSV are great for viewing it inside the editor.

I wrote the markdown for this using the preview feature of vscode to see the rendered version as I wrote it, and markdownlint for live linting.

hyperterm-paste is really nice if you want to copy code snippets into the termianl without executing them, or for copying multiline snippets.


Inspired by https://github.com/lloydstubber/my-wsl-setup, https://daverupert.com/2017/03/my-bash-on-windows-developer-environment/#hyper, and https://nickjanetakis.com/blog/using-wsl-and-mobaxterm-to-create-a-linux-dev-environment-on-windows#wsl-conemu-and-mobaxterm-to-the-rescue

@lego-sharat
Copy link

lego-sharat commented Jun 4, 2018

Thanks for the guide. I have setup vscode and python on wsl. It seems to be working but when I try to navigate to definition of an import, vs code can't open the file as the path it is taking is a wsl path and I guess the path it requires is a windows path. Is there a workaround for this.
Adding the exact error it is
Unable to open 'init.py': File not found (file:///mnt/c/Users/sharat/Documents/projects/testing-python/.venv/lib/python3.6/site-packages/requests/init.py).

@highoncarbs
Copy link

Do iI really need hyper to setup Python ?

@kjohnsen
Copy link

Thank you!!!

@jowadmax
Copy link

@sharat9211 This should be fixable too. For this to work, you'll need update two files slightly.

First, update the last line in .vscode_bats/python3.bat to
   bash.exe -c "TRANSLATE_TO_WINDOWS=1 ~/.pyenv/shims/python %v_params%"

Then, you'll need to update a file located in:
   C:\Users\[user name]\.vscode\extensions\ms-python.python-[version_date_here]\pythonFiles\completion.py

   1) add the following to the top of the file:
      import subprocess

   2) add the following to the top of the _write_response function:

        if 'TRANSLATE_TO_WINDOWS' in os.environ:
            search_results = list(set(re.findall(r'"(/mnt/c/.+?)"', response, re.I)))
            search_results.sort(key = lambda s: s.count("/"), reverse=True)

            for wsl_path in search_results:
                win_path = subprocess.check_output(['wslpath', '-w', '-a', wsl_path]).decode("utf-8")
                if wsl_path[-1] == '/':
                    win_path += "\\"
                win_path = win_path.replace("\n", "").replace("\\", "\\\\")
                response = response.replace(wsl_path, win_path)

Now save the file, restart VS Code, and enjoy following files just as if you were using the Windows Python installation!

@awmv
Copy link

awmv commented Aug 4, 2018

Thanks for that. Everything seems to work so far.
I do have two error messages that remain. Is it possible to get rid of them too? Cheers

Error Messages

@ronanmcallister
Copy link

I wonder if anyone knows how to fix this -- I followed the great instructions exactly, though I must have done something incorrectly as though I can run MS Code using "code &" from within my Ubuntu shell under ~m, when running "vscode . &" in the last part of the procedure, I get "vscode: command not found".

How can I troubleshoot why an executable or script named "vscode" isn't found in my WSL Ubuntu shell?

Windows 10 Enterprise build 1803
"uname -a": Linux DESKTOP 4.4.0-17134-Microsoft #112-Microsoft Thu Jun 07 22:57:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
"arch": x86_64

Any help is appreciated!

Thanks in advance,
Ronan

@kevinpaulconnor
Copy link

kevinpaulconnor commented Sep 18, 2018

@ronanmcallister, I believe that you'd run "vscode" only from Hyper, so if you're using the ubuntu shell, "code" is the correct command.

This guide was incredibly useful, thank you. But I was unable to get the code_pipenv bash script working; it seems like my VSCode requires Windows paths to the .bat files in settings.json, but the code_pipenv script is supplying a linux path (e.g., the script is supplying the path that I might get from pwd, not a path that starts "C:\Users\...python.bat). I'm not enough of a Windows person to easily understand how to fix the bash script to generate windows paths, but when I manually supply windows paths to the .bat files in the settings.json, things seem to work as expected. Switching to windows paths might solve your problem, @robjane. If you are getting an error that Python is not installed, then your WSL/VSCode isn't really connecting correctly with regard to Python.

@fezken
Copy link

fezken commented Nov 12, 2018

I am having the same issue as you @kevinpaulconnor . Have you found a solution in the bash scripts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment