Skip to content

Commit

Permalink
adds a basic clang formatter (Project-MONAI#1006)
Browse files Browse the repository at this point in the history
* adds a basic clang formatter

Signed-off-by: Wenqi Li <[email protected]>

* fixes clang-format version

Signed-off-by: Wenqi Li <[email protected]>

* update to use monai native APIs

Signed-off-by: Wenqi Li <[email protected]>

* update based on comments

Signed-off-by: Wenqi Li <[email protected]>

* run test utils using coverage

Signed-off-by: Wenqi Li <[email protected]>

* testing autoformat

Signed-off-by: Wenqi Li <[email protected]>

* [MONAI] python code formatting

Signed-off-by: monai-bot <[email protected]>

* update based on comments

Signed-off-by: Wenqi Li <[email protected]>

* update based on comments

Signed-off-by: Wenqi Li <[email protected]>

Co-authored-by: monai-bot <[email protected]>
Co-authored-by: Nic Ma <[email protected]>
  • Loading branch information
3 people authored Sep 12, 2020
1 parent 5c9c7b0 commit 543e2a0
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 54 deletions.
88 changes: 88 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ]
IncludeCategories:
- Regex: '^<.*\.h(pp)?>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 2000000
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...
4 changes: 4 additions & 0 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ jobs:
python -c "import torch; print(torch.__version__); print('{} of GPUs available'.format(torch.cuda.device_count()))"
python -c 'import torch; print(torch.rand(5,3, device=torch.device("cuda:0")))'
./runtests.sh --quick
if [ ${{ matrix.environment }} == "PT16+CUDA110" ]; then
# test the clang-format tool downloading once
coverage run -m tests.clang_format_utils
fi
coverage xml
- name: Upload coverage
uses: codecov/codecov-action@v1
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,8 @@ temp/
tests/testing_data/MedNIST*
tests/testing_data/*Hippocampus*

# clang format tool
.clang-format-bin/

# VSCode
.vscode/
2 changes: 1 addition & 1 deletion docs/source/apps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Applications
`Utilities`
-----------

.. autofunction:: check_md5
.. autofunction:: check_hash

.. autofunction:: download_url

Expand Down
98 changes: 58 additions & 40 deletions monai/apps/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,67 @@
gdown, has_gdown = optional_import("gdown", "3.6")


def check_md5(filepath: str, md5_value: Optional[str] = None) -> bool:
def check_hash(filepath: str, val: Optional[str] = None, hash_type: str = "md5") -> bool:
"""
check MD5 signature of specified file.
Verify hash signature of specified file.
Args:
filepath: path of source file to verify MD5.
md5_value: expected MD5 value of the file.
filepath: path of source file to verify hash value.
val: expected hash value of the file.
hash_type: 'md5' or 'sha1', defaults to 'md5'.
"""
if md5_value is not None:
md5 = hashlib.md5()
try:
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
md5.update(chunk)
except Exception as e:
print(f"Exception in check_md5: {e}")
return False
if md5_value != md5.hexdigest():
return False
if val is None:
print(f"Expected {hash_type} is None, skip {hash_type} check for file {filepath}.")
return True
if hash_type.lower() == "md5":
actual_hash = hashlib.md5()
elif hash_type.lower() == "sha1":
actual_hash = hashlib.sha1()
else:
print(f"expected MD5 is None, skip MD5 check for file {filepath}.")

raise NotImplementedError(f"Unknown 'hash_type' {hash_type}.")
try:
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
actual_hash.update(chunk)
except Exception as e:
print(f"Exception in check_hash: {e}")
return False
if val != actual_hash.hexdigest():
print("check_hash failed.")
return False

print(f"Verified '{os.path.basename(filepath)}', {hash_type}: {val}.")
return True


def download_url(url: str, filepath: str, md5_value: Optional[str] = None) -> None:
def download_url(url: str, filepath: str, hash_val: Optional[str] = None, hash_type: str = "md5") -> None:
"""
Download file from specified URL link, support process bar and MD5 check.
Download file from specified URL link, support process bar and hash check.
Args:
url: source URL link to download file.
filepath: target filepath to save the downloaded file.
md5_value: expected MD5 value to validate the downloaded file.
if None, skip MD5 validation.
hash_val: expected hash value to validate the downloaded file.
if None, skip hash validation.
hash_type: 'md5' or 'sha1', defaults to 'md5'.
Raises:
RuntimeError: When the MD5 validation of the ``filepath`` existing file fails.
RuntimeError: When the hash validation of the ``filepath`` existing file fails.
RuntimeError: When a network issue or denied permission prevents the
file download from ``url`` to ``filepath``.
URLError: See urllib.request.urlretrieve.
HTTPError: See urllib.request.urlretrieve.
ContentTooShortError: See urllib.request.urlretrieve.
IOError: See urllib.request.urlretrieve.
RuntimeError: When the MD5 validation of the ``url`` downloaded file fails.
RuntimeError: When the hash validation of the ``url`` downloaded file fails.
"""
if os.path.exists(filepath):
if not check_md5(filepath, md5_value):
raise RuntimeError(f"MD5 check of existing file failed: filepath={filepath}, expected MD5={md5_value}.")
if not check_hash(filepath, hash_val, hash_type):
raise RuntimeError(
f"{hash_type} check of existing file failed: filepath={filepath}, expected {hash_type}={hash_val}."
)
print(f"file {filepath} exists, skip downloading.")
return

Expand Down Expand Up @@ -110,8 +121,8 @@ def download_url(url: str, filepath: str, md5_value: Optional[str] = None) -> No
logging.debug("IO Error - %s" % e)
finally:
if file_size == os.path.getsize(tmp_file_path):
if md5_value and not check_md5(tmp_file_path, md5_value):
raise Exception("Error validating the file against its MD5 hash")
if hash_val and not check_hash(tmp_file_path, hash_val, hash_type):
raise Exception(f"Error validating the file against its {hash_type} hash")
shutil.move(tmp_file_path, filepath)
elif file_size == -1:
raise Exception("Error getting Content-Length from server: %s" % url)
Expand All @@ -128,34 +139,38 @@ def _process_hook(blocknum: int, blocksize: int, totalsize: int):
print(f"download failed from {url} to {filepath}.")
raise e

if not check_md5(filepath, md5_value):
if not check_hash(filepath, hash_val, hash_type):
raise RuntimeError(
f"MD5 check of downloaded file failed: URL={url}, filepath={filepath}, expected MD5={md5_value}."
f"{hash_type} check of downloaded file failed: URL={url}, "
f"filepath={filepath}, expected {hash_type}={hash_val}."
)


def extractall(filepath: str, output_dir: str, md5_value: Optional[str] = None) -> None:
def extractall(filepath: str, output_dir: str, hash_val: Optional[str] = None, hash_type: str = "md5") -> None:
"""
Extract file to the output directory.
Expected file types are: `zip`, `tar.gz` and `tar`.
Args:
filepath: the file path of compressed file.
output_dir: target directory to save extracted files.
md5_value: expected MD5 value to validate the compressed file.
if None, skip MD5 validation.
hash_val: expected hash value to validate the compressed file.
if None, skip hash validation.
hash_type: 'md5' or 'sha1', defaults to 'md5'.
Raises:
RuntimeError: When the MD5 validation of the ``filepath`` compressed file fails.
RuntimeError: When the hash validation of the ``filepath`` compressed file fails.
ValueError: When the ``filepath`` file extension is not one of [zip", "tar.gz", "tar"].
"""
target_file = os.path.join(output_dir, os.path.basename(filepath).split(".")[0])
if os.path.exists(target_file):
print(f"extracted file {target_file} exists, skip extracting.")
return
if not check_md5(filepath, md5_value):
raise RuntimeError(f"MD5 check of compressed file failed: filepath={filepath}, expected MD5={md5_value}.")
if not check_hash(filepath, hash_val, hash_type):
raise RuntimeError(
f"{hash_type} check of compressed file failed: " f"filepath={filepath}, expected {hash_type}={hash_val}."
)

if filepath.endswith("zip"):
zip_file = zipfile.ZipFile(filepath)
Expand All @@ -169,7 +184,9 @@ def extractall(filepath: str, output_dir: str, md5_value: Optional[str] = None)
raise ValueError('Unsupported file extension, available options are: ["zip", "tar.gz", "tar"].')


def download_and_extract(url: str, filepath: str, output_dir: str, md5_value: Optional[str] = None) -> None:
def download_and_extract(
url: str, filepath: str, output_dir: str, hash_val: Optional[str] = None, hash_type: str = "md5"
) -> None:
"""
Download file from URL and extract it to the output directory.
Expand All @@ -178,9 +195,10 @@ def download_and_extract(url: str, filepath: str, output_dir: str, md5_value: Op
filepath: the file path of compressed file.
output_dir: target directory to save extracted files.
default is None to save in current directory.
md5_value: expected MD5 value to validate the downloaded file.
if None, skip MD5 validation.
hash_val: expected hash value to validate the downloaded file.
if None, skip hash validation.
hash_type: 'md5' or 'sha1', defaults to 'md5'.
"""
download_url(url=url, filepath=filepath, md5_value=md5_value)
extractall(filepath=filepath, output_dir=output_dir, md5_value=md5_value)
download_url(url=url, filepath=filepath, hash_val=hash_val, hash_type=hash_type)
extractall(filepath=filepath, output_dir=output_dir, hash_val=hash_val, hash_type=hash_type)
12 changes: 12 additions & 0 deletions runexamples.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
#!/bin/bash

# Copyright 2020 MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e
# script for running the examples

Expand Down
Loading

0 comments on commit 543e2a0

Please sign in to comment.