Instructions for the Development tools (VS Code) train track
There are many different ways to install Python depending on your OS and preferences. Real Python give a nice list of them here https://realpython.com/installing-python/
For the uninitated, there is a substantial difference between Python versions 2 & 3, which means most code written for one won't work for the other by default. Python 2 has been deprecated since 2020, so we will be working with Python 3 exclusively. Therefore, please make sure the Python you have installed is version 3 (e.g python --version
), or ensure you always use the python3
and pip3
aliases.
Installing VS Code itself is pretty straightforward. Just download and install it from the visual studio website.
VS Code is a general purpose code editor that can be configured to be used for virtually any programming language. This is supported by a wide array of 3rd party extensions. New extensions can be added to your workspace by clicking on the "Extensions" tab on the bar on the LHS of the screen
We will be using the "Python" extension today so please make sure it is installed
One of the major strengths of Python compared to other programming languages is the vast amount of 3rd party packages available, which come in all shapes and sizes (you can even write one yourself pretty easily!). However, with this vastness comes a fair bit of complexity in managing shared dependencies, particularly ones that require specific versions. To get around this, it is common to create a fresh "environment" for each project/workspace and install only the packages you need for it in order to avoid clashes.
There are two popular ways to create virtual environments in Python either using the built-in venv
module or the more general software package manager Anaconda (which Chris explained how to use in the "Intro to Python" train-track earlier this week). Since we will be relying on some packages that have C dependencies we will use Anaconda to manage them for this tutorial.
To do this you first create a new conda environment
conda create --name bhg-devtools
and then activate it, which you need to do before installing or using your packages every time you open a new terminal
conda activate bhg-devtools
The packages you will need to install inside your Conda environment for today's session are
- dipy
- click
- pytest
by using
conda install -c conda-forge dipy click pytest
After setting up your virtual environment you will need to configure VS Code to use it by selecting the Python interpreter for your workspace (see https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
Create a fork of the https://github.com/brainhack-aus/dev-tools-train-track repository (i.e. this one) by using the source control tab
and open it as a new project.
Should see virtually empty project with just a README file and gitignore.
You will then pull some changes I have made upstream using the source control tab.
Use the examples shown in the Dipy Quick Start Guide to create three (trivial) methods in a new module, i.e. a Python file called intensify.py
, to load a nifti file, multiply its intensity, and save it back to disk.
To get some test data to play with, first download a test dataset from Dipy in a Python terminal
>>> from dipy.data import fetch_sherbrooke_3shell
>>> fetch_sherbrooke_3shell()
This will download a DWI scan in NIfTI format to the $HOME/.dipy/fetch_sherbrooke_3shell
directory.
Use click to create a script in a separate file that imports and runs your method in a command line tool that takes the following arguments/options
- input file path (argument)
- output file path (argument)
- intensity (option with default value)
If you are feeling confident you might want to work with this cool example to estimate the SNR of diffusion-weighted images https://dipy.org/documentation/1.4.1./examples_built/snr_in_cc/#example-snr-in-cc.
Open new terminal by pressing Ctrl-Shift-P (Cmd-Shift-P on Mac), typing "terminal" and selecting "new terminal". This is just a normal terminal so you can run your command line tool by
python3 your-script.py
Create a launch.json file from the "Run and Debug" tab in the LHS menu
Create a directory called tests
and a file called test_intensify.py
within it.
Write a simple test that tests a small part of your code e.g.
import numpy
from intensify import modulate_image
def test_intensify():
orig_image = numpy.ones([10,10])
intensified_image = modulate_image(orig_image, 10, 5)
assert np.max(my_image) == np.max(orig_image) * 10 + 5
Add a conftest.py
file to allow unittests to break on exceptions in debug mode
import os
import pytest
# For debugging in IDE's don't catch raised exceptions and let the IDE
# break at it
if os.getenv('_PYTEST_RAISE', "0") != "0":
@pytest.hookimpl(tryfirst=True)
def pytest_exception_interact(call):
raise call.excinfo.value
@pytest.hookimpl(tryfirst=True)
def pytest_internalerror(excinfo):
raise excinfo.value
If you keep writing the same code across multiple tests (e.g. creating a test image) you can put them in a Pytest fixture in your conftest.py, e.g.
import os
import pytest
import shutil
from pathlib import Path
from tempfile import mkdtemp
import numpy
# For debugging in IDE's don't catch raised exceptions and let the IDE
# break at it
if os.getenv('_PYTEST_RAISE', "0") != "0":
@pytest.hookimpl(tryfirst=True)
def pytest_exception_interact(call):
raise call.excinfo.value
@pytest.hookimpl(tryfirst=True)
def pytest_internalerror(excinfo):
raise excinfo.value
@pytest.fixture
def test_image():
return numpy.ones([10,10])
which can then be provided as an argument to your test function automagically (i.e. the argument name must match the name of the fixture), e.g.
from intensify import modulate_image
def test_intensify(test_image)
intensified_image = modulate_image(test_image, 10, 5)
assert np.max(my_image) == np.max(test_image) * 10 + 5