Contributing a source code change¶
This tutorial will take you through the process of:
Downloading the current source code
Installing required libraries (if any)
Running the test suite
Creating a new git branch
Making a change and documenting it
Previewing your changes
Committing the changes and making a pull request
Download the current source code¶
Sign up to GitHub and fork pydicom
Install Git. If you’re new to Git, the Django project has a good introduction on working with Git and GitHub. You can also take a look at the GitHub branch-based workflow
Using the command line,
cdto the directory where you want your local copy of pydicom to live. The source code can then be downloaded using:
$ git clone https://github.com/YourUsername/pydicom.git
(Optional) We recommend that you install your development copy of pydicom in a virtual environment as this allows you to test different combinations of Python and installed packages. See the virtual environments tutorial for more information.
Create a new virtualenv
X.Yis an installed Python version such as 3.7 and
/path/to/pythonX.Yis the path to the corresponding Python executable:
$ mkvirtualenv -p /path/to/pythonX.Y pydX.Y
Install the cloned copy of pydicom (
-efor editable mode):
$ pip install -e pydicom/
(Optional) Install required libraries¶
If you’re making changes to one of the pixel data handlers you’ll need to install NumPy as well as the library the handler is based on.
For example, if you’re working on the
you’ll also need to install Pillow:
$ pip install numpy pillow
See the installation page for details on installing the optional libraries.
Install pytest and run the test suite¶
When making changes to pydicom it’s important that your changes don’t accidentally introduce bugs into other areas of the code. In order to check that everything still works afterwards, you should run our test suite, which is based on pytest.
Install and run pytest:
$ pip install pytest $ cd pydicom/pydicom/tests $ pytest
While the tests are running you’ll see a filename followed by a stream of characters that represent the result of each test. A dot means the test passed, F indicates a failure, E that an error occurred during the test, s that the test was skipped (usually due to a missing optional library) and x that the test failed as expected.
Once the tests are complete you should get a short summary of the results. At this stage the entire test suite should pass. If you get any failures or errors you should check the issue tracker for any relevant issues or create a new one if there are none.
Create a new branch¶
Create a new branch
new-uid for your changes (you can choose any name
that you want instead). Any changes made in this branch will be specific to
it and won’t affect the main copy (the
master branch) of the code:
$ git checkout -b new-uid
Write tests for your changes¶
If a change is to be accepted into pydicom it usually has to include tests. For bug fixes you should write a regression test that reproduces the bug. For new features you’ll need to include tests that ensure the features work as intended.
If you’ve never had to write tests before they can seem pretty daunting, especially if you’re also learning how to use pytest from scratch. You may find the following resources useful:
Take a look at the existing pydicom test suite and see how the tests are written. There are examples for writing a single test, a group of related tests, testing for exceptions, capturing log output, testing for warnings, and running parametrized tests.
Dive Into Python has a very nice section on unit testing (however it uses
unittestinstead of pytest).
The pytest documentation may also be helpful
If you’re still having trouble writing a test for something, once you’ve created a pull request (to be discussed a bit later) add a comment asking for help.
Let’s say we wanted to add a new pre-defined UID
to pydicom with a value of
188.8.131.52.500. We’d first add a new test at the
bottom of test_uid.py:
def test_new_uid(): """Test uid.NewDefinedUID.""" from pydicom.uid import NewDefinedUID assert '184.108.40.206.500' == NewDefinedUID
Since we haven’t made any modification to the actual source code, when we run the tests we should get a failure:
$ pytest test_uid.py
def test_new_uid(): """Test uid.NewDefinedUID.""" > from pydicom.uid import NewDefinedUID E ImportError: cannot import name 'NewDefinedUID' test_uid.py:380: ImportError
If all the tests passed then make sure you’ve added the test to the correct file and that the test itself is written correctly.
Make a code change and document it¶
Next we’ll make changes to the actual source code. Open uid.py in a text editor and around line 236 make the following changes:
RLELossless = UID('1.2.840.10008.1.2.5') """1.2.840.10008.1.2.5""" # **Add this** NewDefinedUID = UID('220.127.116.11.500') """18.104.22.168.500"""
"""22.214.171.124.500""" is the docstring for our new UID. In order for
it to be included in the API reference documentation we’ll also need to update
JPEG2000MultiComponentLossless JPEG2000MultiComponent RLELossless NewDefinedUID
When making changes, and especially when adding new features, it’s important that they’re documented. It’s very difficult for users to find and understand how to use code that hasn’t been documented, or whose documentation contains errors. For more information on how to properly document pydicom see writing documentation.
Now we run the tests again so we can see whether or not the code we added is working:
$ pytest test_uid.py
Everything should pass. If it doesn’t, make sure you’ve correctly added the
new UID. Once you’re happy that the tests in
test_uid.py are working you
should make sure the entire test suite passes:
Preview your changes¶
It’s a good idea to go through all the changes you’ve made by first staging and then displaying the difference between the current copy and the initial version we first checked out with:
$ git add --all $ git diff --cached
You can scroll through the output using the up and down keys and quit with q. Lines with a - in front will be removed and lines with a + added. If everything looks good then it’s time to commit the changes.
Commit your changes and make a pull request¶
To commit the changes:
$ git commit
This will open a text editor so you can add the commit message. Alternatively, if you only want a short commit message you can do:
$ git commit -m "Add NewDefinedUID"
Which will commit with the message “Add NewDefinedUID”. After committing the patch, send it to your fork:
$ git push origin new-uid
You can create a pull request by visiting the pydicom GitHub page where you should see your branch under “Your recently push
branches”. Click “Compare & pull request” and fill out the title (with a
[WIP] prefix, i.e.
[WIP] Add NewDefinedUID to uid.py) and follow the
instructions in the main entry window.
To submit the pull request (PR) for real - please don’t do this for this example! - then on the next page you would click “Create pull request”. Creating the PR would automatically start our checks; that the tests pass and the test coverage is good, that the documentation builds OK, etc.
If all the checks passed and you were happy with your changes, you’d change
the PR title prefix to
[MRG]. This would indicate that you considered the
PR ready to be reviewed and merged into the main branch. You could also ask
for a review or help at any point after creating the PR.
What happens next?¶
One or more reviewers would look at your pull request and may make suggestions,
ask for clarification or request changes. Once the reviewers were happy,
the pull request would be approved and your changes merged into the
master branch where they would become part of pydicom.
However, because this is just an example, all we’re going to do is clean up the
changes we’ve made. First we switch back to the
$ git checkout master
We delete the local copy of the branch we created:
$ git branch -d new-uid
And lastly we delete the remote copy on GitHub. Go to
https://github.com/YourUsername/pydicom/branches, find the
branch and click the corresponding red bin icon. All done!