Skip to content

Commit f41c7fe

Browse files
authored
Merge pull request #84 from ue-sho/setup-pre-commit-with-ruff
Setup pre commit with ruff
2 parents 9389e6a + c7289b7 commit f41c7fe

File tree

12 files changed

+935
-800
lines changed

12 files changed

+935
-800
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
merge_group:
66

77
jobs:
8-
launch-unit-tests:
8+
lint-and-test:
99
runs-on: ubuntu-latest
1010

1111
strategy:
@@ -23,21 +23,16 @@ jobs:
2323
uses: actions/setup-python@v5
2424
with:
2525
python-version: ${{ matrix.python-version }}
26-
27-
- name: Cache Python dependencies
28-
uses: actions/cache@v4
29-
id: pip-cache
30-
with:
31-
path: ~/.cache/pip
32-
key: pip-${{ matrix.python-version }}-${{ hashFiles('**/setup.py') }}
33-
restore-keys: |
34-
pip-${{ matrix.python-version }}-${{ hashFiles('**/setup.py') }}
26+
cache: 'pip'
27+
cache-dependency-path: setup.py
3528

3629
- name: Install python dependencies
37-
if: steps.pip-cache.outputs.cache-hit != 'true'
30+
run: pip install -e .[dev]
31+
32+
- name: Check code style
3833
run: |
39-
python -m pip install --upgrade pip
40-
pip install -e .[dev]
34+
ruff check
35+
ruff format --check
4136
4237
- name: Run test
4338
run: pytest

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.4.4
4+
hooks:
5+
- id: ruff
6+
args: [ --fix ]
7+
- id: ruff-format

README.md

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,44 @@ Dicom fields are separated into different groups. Each groups will be anonymized
2525

2626
Installation can be done via pip `pip install dicom-anonymizer` or conda `conda install -c conda-forge dicom-anonymizer`.
2727

28-
# How to test it?
29-
- One time set up:
30-
- virtual environment for this package and activate it. For
31-
example set up using `virtualenv venv` and activate using
32-
`venv\Scripts\activate.bat` (on Windows)
33-
- Install editable version and development requirements using
34-
`pip install -e .[dev]`
35-
- Run unit test using `pytest`
28+
29+
# Local Development Setup
30+
31+
To get started with local development, follow these steps:
32+
33+
1. Create a Virtual Environment:
34+
- On Windows:
35+
```sh
36+
virtualenv env
37+
.\env\Scripts\activate.bat
38+
```
39+
- On MacOS/Linux:
40+
```sh
41+
python -m venv env
42+
source env/bin/activate
43+
```
44+
45+
2. Install Dependencies:
46+
- Install an editable version of the package and the development requirements:
47+
```sh
48+
pip install -e .[dev]
49+
```
50+
51+
3. Set Up Pre-Commit Hooks:
52+
- Install the pre-commit hooks to ensure code quality:
53+
```sh
54+
pre-commit install
55+
```
56+
57+
58+
## How to test it?
59+
60+
To run the unit tests, use the following command:
61+
62+
```sh
63+
pytest
64+
```
65+
3666

3767
# How to build it?
3868
These instructions rely on wheel build-package format. Install it if you have not done it already using:

dicomanonymizer/anonymizer.py

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
23
if sys.version_info >= (3, 8):
34
import importlib.metadata as metadata
45
else:
@@ -8,25 +9,32 @@
89
import ast
910
import json
1011
import os
11-
import sys
1212
import tqdm
1313

14-
from dicomanonymizer.simpledicomanonymizer import anonymize_dicom_file, ActionsMapNameFunctions
14+
from dicomanonymizer.simpledicomanonymizer import (
15+
anonymize_dicom_file,
16+
ActionsMapNameFunctions,
17+
)
1518

1619

1720
def isDICOMType(filePath):
1821
"""
1922
:returns True if input file is a DICOM File. False otherwise.
2023
"""
2124
try:
22-
with open(filePath, 'rb') as tempFile:
25+
with open(filePath, "rb") as tempFile:
2326
tempFile.seek(0x80, os.SEEK_SET)
24-
return tempFile.read(4) == b'DICM'
27+
return tempFile.read(4) == b"DICM"
2528
except IOError:
2629
return False
2730

2831

29-
def anonymize(input_path: str, output_path: str, anonymization_actions: dict, delete_private_tags: bool) -> None:
32+
def anonymize(
33+
input_path: str,
34+
output_path: str,
35+
anonymization_actions: dict,
36+
delete_private_tags: bool,
37+
) -> None:
3038
"""
3139
Read data from input path (folder or file) and launch the anonymization.
3240
@@ -37,37 +45,42 @@ def anonymize(input_path: str, output_path: str, anonymization_actions: dict, de
3745
:param deletePrivateTags: Whether to delete private tags.
3846
"""
3947
# Get input arguments
40-
input_folder = ''
41-
output_folder = ''
48+
input_folder = ""
49+
output_folder = ""
4250

4351
if os.path.isdir(input_path):
4452
input_folder = input_path
4553

4654
if os.path.isdir(output_path):
4755
output_folder = output_path
48-
if input_folder == '':
56+
if input_folder == "":
4957
output_path = os.path.join(output_folder, os.path.basename(input_path))
5058

51-
if input_folder != '' and output_folder == '':
52-
print('Error, please set a correct output folder path')
59+
if input_folder != "" and output_folder == "":
60+
print("Error, please set a correct output folder path")
5361
sys.exit()
5462

5563
# Generate list of input file if a folder has been set
5664
input_files_list = []
5765
output_files_list = []
58-
if input_folder == '':
66+
if input_folder == "":
5967
input_files_list.append(input_path)
6068
output_files_list.append(output_path)
6169
else:
6270
files = os.listdir(input_folder)
6371
for fileName in files:
64-
if isDICOMType(input_folder + '/' + fileName):
65-
input_files_list.append(input_folder + '/' + fileName)
66-
output_files_list.append(output_folder + '/' + fileName)
72+
if isDICOMType(input_folder + "/" + fileName):
73+
input_files_list.append(input_folder + "/" + fileName)
74+
output_files_list.append(output_folder + "/" + fileName)
6775

6876
progress_bar = tqdm.tqdm(total=len(input_files_list))
6977
for cpt in range(len(input_files_list)):
70-
anonymize_dicom_file(input_files_list[cpt], output_files_list[cpt], anonymization_actions, delete_private_tags)
78+
anonymize_dicom_file(
79+
input_files_list[cpt],
80+
output_files_list[cpt],
81+
anonymization_actions,
82+
delete_private_tags,
83+
)
7184
progress_bar.update(1)
7285

7386
progress_bar.close()
@@ -83,7 +96,9 @@ def parse_tag_actions_arguments(t_arguments: list, new_anonymization_actions: di
8396
for current_tag_parameters in t_arguments:
8497
nb_parameters = len(current_tag_parameters)
8598
if nb_parameters < 2:
86-
raise ValueError("-t parameters should always have 2 values: tag and action")
99+
raise ValueError(
100+
"-t parameters should always have 2 values: tag and action"
101+
)
87102

88103
tag = current_tag_parameters[0]
89104
action_name = current_tag_parameters[1]
@@ -96,9 +111,15 @@ def parse_tag_actions_arguments(t_arguments: list, new_anonymization_actions: di
96111
action_arguments = current_tag_parameters[2:]
97112

98113
if len(action_arguments) != action_object.number_of_expected_arguments:
99-
raise ValueError(f"Wrong number of arguments for action {action_name}: found {len(action_arguments)}")
100-
101-
action = action_object.function if not len(action_arguments) else action_object.function(action_arguments)
114+
raise ValueError(
115+
f"Wrong number of arguments for action {action_name}: found {len(action_arguments)}"
116+
)
117+
118+
action = (
119+
action_object.function
120+
if not len(action_arguments)
121+
else action_object.function(action_arguments)
122+
)
102123
tag_list = [ast.literal_eval(tag)]
103124

104125
new_anonymization_actions.update({tag: action for tag in tag_list})
@@ -116,7 +137,7 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions):
116137
for tag, action_or_options in data.items():
117138
if isinstance(action_or_options, dict):
118139
try:
119-
action_name = action_or_options.pop('action')
140+
action_name = action_or_options.pop("action")
120141
except KeyError as e:
121142
raise ValueError(f"Missing field in tag {tag}: {e.args[0]}")
122143
try:
@@ -125,7 +146,8 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions):
125146
raise ValueError(f"Action {action_name} is not recognized.")
126147
if len(action_or_options) != action_object.number_of_expected_arguments:
127148
raise ValueError(
128-
f"Wrong number of arguments for action {action_name}: found {len(action_or_options)}")
149+
f"Wrong number of arguments for action {action_name}: found {len(action_or_options)}"
150+
)
129151
action = action_object.function(action_or_options)
130152
else:
131153
try:
@@ -140,18 +162,35 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions):
140162
def main():
141163
version_info = metadata.version("dicom_anonymizer")
142164
parser = argparse.ArgumentParser(add_help=True)
143-
parser.add_argument('input', help='Path to the input dicom file or input directory which contains dicom files')
144165
parser.add_argument(
145-
'output', help='Path to the output dicom file or output directory which will contains dicom files')
166+
"input",
167+
help="Path to the input dicom file or input directory which contains dicom files",
168+
)
169+
parser.add_argument(
170+
"output",
171+
help="Path to the output dicom file or output directory which will contains dicom files",
172+
)
173+
parser.add_argument(
174+
"-t",
175+
action="append",
176+
nargs="*",
177+
help="tags action : Defines a new action to apply on the tag.'regexp' action takes two arguments: "
178+
"1. regexp to find substring 2. the string that will replace the previous found string",
179+
)
180+
parser.add_argument(
181+
"-v", "--version", action="version", version=f"%(prog)s {version_info}"
182+
)
183+
parser.add_argument(
184+
"--dictionary",
185+
action="store",
186+
help="File which contains a dictionary that can be added to the original one",
187+
)
146188
parser.add_argument(
147-
'-t', action='append', nargs='*',
148-
help='tags action : Defines a new action to apply on the tag.\'regexp\' action takes two arguments: '
149-
'1. regexp to find substring 2. the string that will replace the previous found string')
150-
parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {version_info}')
151-
parser.add_argument('--dictionary', action='store',
152-
help='File which contains a dictionary that can be added to the original one')
153-
parser.add_argument('--keepPrivateTags', action='store_true', dest='keepPrivateTags',
154-
help='If used, then private tags won\'t be deleted')
189+
"--keepPrivateTags",
190+
action="store_true",
191+
dest="keepPrivateTags",
192+
help="If used, then private tags won't be deleted",
193+
)
155194
parser.set_defaults(keepPrivateTags=False)
156195
args = parser.parse_args()
157196

@@ -168,8 +207,10 @@ def main():
168207
parse_dictionary_argument(args.dictionary, new_anonymization_actions)
169208

170209
# Launch the anonymization
171-
anonymize(input_path, output_path, new_anonymization_actions, not args.keepPrivateTags)
210+
anonymize(
211+
input_path, output_path, new_anonymization_actions, not args.keepPrivateTags
212+
)
172213

173214

174-
if __name__ == '__main__':
215+
if __name__ == "__main__":
175216
main()

0 commit comments

Comments
 (0)