Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hook to verify RAPIDS version isn't hard-coded #13

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,14 @@
language: python
types: [shell]
args: [--fix]
- id: verify-rapids-metadata
name: RAPIDS metadata
description: make sure RAPIDS metadata has template file or isn't hard-coded
entry: verify-rapids-metadata
language: python
types: [text]
exclude: |
(?x)
^VERSION$|
[.]rapids_metadata_template$
args: [--fix]
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ classifiers = [
requires-python = ">=3.9"
dependencies = [
"bashlex",
"packaging",
]

[project.optional-dependencies]
Expand All @@ -42,6 +43,7 @@ dev = [

[project.scripts]
verify-conda-yes = "rapids_pre_commit_hooks.shell.verify_conda_yes:main"
verify-rapids-metadata = "rapids_pre_commit_hooks.rapids_metadata:main"

[tool.setuptools]
packages = { "find" = { where = ["src"] } }
Expand Down
2 changes: 1 addition & 1 deletion src/rapids_pre_commit_hooks/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __repr__(self):
return (
"LintWarning("
+ f"pos={self.pos}, "
+ f"msg={self.msg}, "
+ f"msg={repr(self.msg)}, "
+ f"replacements={self.replacements})"
)

Expand Down
76 changes: 76 additions & 0 deletions src/rapids_pre_commit_hooks/rapids_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# 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.

import re

from packaging.version import Version

from .lint import LintMain


def check_rapids_version(linter, rapids_version_re):
for match in rapids_version_re.finditer(linter.content):
linter.add_warning(
match.span(),
"do not hard-code RAPIDS version; dynamically read from VERSION file or "
f'write a "{linter.filename}.rapids_metadata_template" file',
)


def check_rapids_metadata():
with open("VERSION") as f:
version = Version(f.read())

rapids_version_re = re.compile(
rf"{version.major:02}\.{version.minor:02}(\.{version.micro:02})?"
)

def the_check(linter, args):
try:
with open(f"{linter.filename}.rapids_metadata_template") as f:
template_content = f.read()
except FileNotFoundError:
template_content = None

if template_content is None:
check_rapids_version(linter, rapids_version_re)
else:
template_replacement = template_content.format(
RAPIDS_VERSION_MAJOR=f"{version.major:02}",
RAPIDS_VERSION_MINOR=f"{version.minor:02}",
RAPIDS_VERSION_PATCH=f"{version.micro:02}",
RAPIDS_VERSION=(
f"{version.major:02}.{version.minor:02}.{version.micro:02}"
),
RAPIDS_VERSION_MAJOR_MINOR=f"{version.major:02}.{version.minor:02}",
)

if linter.content != template_replacement:
linter.add_warning(
(0, len(linter.content)),
f'file does not match template replacement from "{linter.filename}'
'.rapids_metadata_template"',
).add_replacement((0, len(linter.content)), template_replacement)

return the_check


def main():
m = LintMain()
with m.execute() as ctx:
ctx.add_check(check_rapids_metadata())


if __name__ == "__main__":
main()
140 changes: 140 additions & 0 deletions test/rapids_pre_commit_hooks/test_rapids_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# 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.

import io
from unittest.mock import patch

import pytest

from rapids_pre_commit_hooks.lint import Linter
from rapids_pre_commit_hooks.rapids_metadata import check_rapids_metadata


class TestRAPIDSMetadata:
def mock_files(self, files):
def new_open(filename, mode="r"):
assert mode == "r"

try:
content = files[filename]
except KeyError:
raise FileNotFoundError

return io.StringIO(content)

return patch("builtins.open", new_open)

def test_mock_files(self):
with self.mock_files({"file.txt": "Hello"}):
with open("file.txt") as f:
assert f.read() == "Hello"
with pytest.raises(FileNotFoundError):
open("nonexistent.txt")
with pytest.raises(AssertionError):
open("file.txt", "rb")
with pytest.raises(AssertionError):
open("file.txt", "w")

def test_template_file(self):
FILES = {
"VERSION": "24.04.00\n",
"file.txt.rapids_metadata_template": """This file contains RAPIDS metadata
Full version is {RAPIDS_VERSION}
Major-minor version is {RAPIDS_VERSION_MAJOR_MINOR}
Major version is {RAPIDS_VERSION_MAJOR}
Minor version is {RAPIDS_VERSION_MINOR}
Patch version is {RAPIDS_VERSION_PATCH}
Hard-coded version is 24.04.00
Old hard-coded version is 24.02.00
Brace literal is {{RAPIDS_VERSION}}
""",
}

CORRECT_CONTENT = CONTENT = """This file contains RAPIDS metadata
Full version is 24.04.00
Major-minor version is 24.04
Major version is 24
Minor version is 04
Patch version is 00
Hard-coded version is 24.04.00
Old hard-coded version is 24.02.00
Brace literal is {RAPIDS_VERSION}
"""

linter = Linter("file.txt", CONTENT)
with self.mock_files(FILES):
checker = check_rapids_metadata()
checker(linter, None)
assert linter.warnings == []

CONTENT = """This file contains RAPIDS metadata
Full version is 24.02.00
Major-minor version is 24.02
Major version is 24
Minor version is 02
Patch version is 00
Hard-coded version is 24.04.00
Old hard-coded version is 24.02.00
Brace literal is {RAPIDS_VERSION}
"""
expected_linter = Linter("file.txt", CONTENT)
expected_linter.add_warning(
(0, len(CONTENT)),
"file does not match template replacement from "
'"file.txt.rapids_metadata_template"',
).add_replacement((0, len(CONTENT)), CORRECT_CONTENT)

linter = Linter("file.txt", CONTENT)
with self.mock_files(FILES):
checker = check_rapids_metadata()
checker(linter, None)
assert linter.warnings == expected_linter.warnings

def test_no_template_file(self):
FILES = {
"VERSION": "24.04.00\n",
}

CONTENT = """This file contains RAPIDS metadata
Full version is 24.04.00
Major-minor version is 24.04
Major version is 24
Minor version is 04
Patch version is 00
Hard-coded version is 24.04.00
Old hard-coded version is 24.02.00
Brace literal is {RAPIDS_VERSION}
"""
expected_linter = Linter("file.txt", CONTENT)
expected_linter.add_warning(
(51, 59),
"do not hard-code RAPIDS version; dynamically read from VERSION file or "
'write a "file.txt.rapids_metadata_template" file',
)
expected_linter.add_warning(
(83, 88),
"do not hard-code RAPIDS version; dynamically read from VERSION file or "
'write a "file.txt.rapids_metadata_template" file',
)
expected_linter.add_warning(
(171, 179),
"do not hard-code RAPIDS version; dynamically read from VERSION file or "
'write a "file.txt.rapids_metadata_template" file',
)

linter = Linter("file.txt", CONTENT)
with self.mock_files(FILES):
checker = check_rapids_metadata()
checker(linter, None)
assert linter.warnings == expected_linter.warnings