diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 785ea389..e15299af 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -54,6 +54,7 @@ jobs: - uses: ansys/actions/tests-pytest@v5 with: pytest-extra-args: "--cov=ansys.pre_commit_hooks --cov-report=term --cov-report=html:.cov/html" + python-version: ${{ matrix.python-version }} doc-build: name: "Build documentation" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 30b73589..397cacec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,13 @@ repos: - id: check-github-workflows - repo: https://github.com/ansys/pre-commit-hooks - rev: v0.2.4 - hooks: - - id: add-license-headers \ No newline at end of file + rev: v0.2.6 + hooks: + - id: add-license-headers + args: + - --start_year=2023 + exclude: | + (?x)^( + tests/test_reuse_files/bad_chars.py | + tests/test_reuse_files/index_error.scss + )$ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 56321513..fb9ed93d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ This document follows the conventions laid out in [Keep a CHANGELOG](https://keepachangelog.com/en/1.0.0). +## [Unreleased]() + +### Fixed + +- Fix pytest python versions and fileinput [#118](https://github.com/ansys/pre-commit-hooks/pull/118) + ## [0.2.6](https://github.com/ansys/pre-commit-hooks/releases/tag/v0.2.6) - January 11 2024 ### Added diff --git a/src/ansys/pre_commit_hooks/add_license_headers.py b/src/ansys/pre_commit_hooks/add_license_headers.py index 30bae08a..dccdfdc1 100644 --- a/src/ansys/pre_commit_hooks/add_license_headers.py +++ b/src/ansys/pre_commit_hooks/add_license_headers.py @@ -32,6 +32,7 @@ import json import os import pathlib +from platform import python_version import shutil import sys from tempfile import NamedTemporaryFile @@ -382,10 +383,17 @@ def add_hook_changes(before_hook: str, after_hook: str) -> None: before_hook_lines = before_hook_file.readlines() found_reuse_info = False + # Check if python version is 3.9 since fileinput.input() + # does not support the "encoding" keyword + if "3.9" in python_version(): + file = fileinput.input(after_hook, inplace=True) + else: + file = fileinput.input(after_hook, inplace=True, encoding="utf8") + # Copy file content before add-license-header was run into # the file after add-license-header was run. # stdout is redirected into the file if inplace is True - for line in fileinput.input(after_hook, inplace=True, encoding="utf8"): + for line in file: # Copy the new reuse lines into the file if _util.contains_reuse_info(line): count += 1 diff --git a/test-repo b/test-repo new file mode 160000 index 00000000..a59a0587 --- /dev/null +++ b/test-repo @@ -0,0 +1 @@ +Subproject commit a59a0587ae6746ae9e8eca1780d8176ea933a4e7 diff --git a/tests/test_reuse.py b/tests/test_reuse.py index 125c9ba4..87f4ebf2 100644 --- a/tests/test_reuse.py +++ b/tests/test_reuse.py @@ -58,12 +58,12 @@ def set_up_repo(tmp_path, template_path, template_name, license_path, license_na def make_asset_dirs(tmp_path, template_path, template_name, license_path, license_name): """Make asset directories if using a custom license or template.""" - if "ansys" not in template_name: + if template_name != "ansys.jinja2": reuse_dir = os.path.join(tmp_path, ".reuse", "templates") os.makedirs(reuse_dir) shutil.copyfile(template_path, f"{reuse_dir}/{template_name}") - if "MIT" not in license_name: + if license_name != "MIT.txt": license_dir = os.path.join(tmp_path, "LICENSES") os.makedirs(license_dir) shutil.copyfile(license_path, f"{license_dir}/{license_name}") @@ -96,7 +96,7 @@ def add_argv_run(repo, tmp_file, custom_args): def check_ansys_header(file_name): """Check file contains all copyright and license header components.""" - file = open(file_name, "r") + file = open(file_name, "r", encoding="utf8") count = 0 for line in file: count += 1 @@ -175,8 +175,8 @@ def test_custom_args(tmp_path: pytest.TempPathFactory): # Set template and license names template_name = "test_template.jinja2" license_name = "ECL-1.0.txt" - template_path = os.path.join(REPO_PATH, "tests", "templates", template_name) - license_path = os.path.join(REPO_PATH, "tests", "LICENSES", license_name) + template_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "templates", template_name) + license_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "LICENSES", license_name) # Set up git repository in tmp_path with temporary file repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name) @@ -291,7 +291,7 @@ def test_no_license_check(tmp_path: pytest.TempPathFactory): # Set template and license names template_name = "copyright_only.jinja2" license_name = "MIT.txt" - template_path = os.path.join(REPO_PATH, "tests", "templates", template_name) + template_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "templates", template_name) license_path = os.path.join(REPO_PATH, "LICENSES", license_name) # Set up git repository in tmp_path with temporary file @@ -364,8 +364,8 @@ def test_update_changed_header(tmp_path: pytest.TempPathFactory): # Set template and license names template_name = "test_template.jinja2" license_name = "ECL-1.0.txt" - template_path = os.path.join(REPO_PATH, "tests", "templates", template_name) - license_path = os.path.join(REPO_PATH, "tests", "LICENSES", license_name) + template_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "templates", template_name) + license_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "LICENSES", license_name) # Set up git repository in tmp_path with temporary file repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name) @@ -425,8 +425,8 @@ def test_missing_licenses(tmp_path: pytest.TempPathFactory): # Set template and license names template_name = "test_template.jinja2" license_name = "ECL-1.0.txt" - template_path = os.path.join(REPO_PATH, "tests", "templates", template_name) - license_path = os.path.join(REPO_PATH, "tests", "LICENSES", license_name) + template_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "templates", template_name) + license_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "LICENSES", license_name) # Set up git repository in tmp_path with temporary file repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name) @@ -496,3 +496,95 @@ def test_copy_assets(tmp_path: pytest.TempPathFactory): assert add_argv_run(repo, new_files, new_files) == 1 check_ansys_header(tmp_file) + + +def test_bad_chars(tmp_path: pytest.TempPathFactory): + # Set template and license names + template_name = "ansys.jinja2" + license_name = "MIT.txt" + bad_chars_name = "bad_chars.py" + template_path = os.path.join(REPO_PATH, ".reuse", "templates", template_name) + license_path = os.path.join(REPO_PATH, "LICENSES", license_name) + + # Change dir to tmp_path + os.chdir(tmp_path) + + # Make asset directories if using a custom license or template + # Asset directories are .reuse and LICENSES + make_asset_dirs(tmp_path, template_path, template_name, license_path, license_name) + + # Set up git repository in tmp_path + git.Repo.init(tmp_path) + repo = git.Repo(tmp_path) + repo.index.commit("initialized git repo for tmp_path") + + # Copy file with bad characters to git repository + shutil.copyfile( + os.path.join(REPO_PATH, "tests", "test_reuse_files", bad_chars_name), bad_chars_name + ) + + # Assert the hook failed + assert add_argv_run(repo, bad_chars_name, [bad_chars_name]) == 1 + + # Assert the hook added the license header correctly + check_ansys_header(bad_chars_name) + + os.chdir(REPO_PATH) + + +def test_index_exception(tmp_path: pytest.TempPathFactory): + # Set template and license names + template_name = "copyright_only.jinja2" + license_name = "MIT.txt" + test_filename = "index_error.scss" + template_path = os.path.join(REPO_PATH, "tests", "test_reuse_files", "templates", template_name) + license_path = os.path.join(REPO_PATH, "LICENSES", license_name) + + # Change dir to tmp_path + os.chdir(tmp_path) + + # Make asset directories if using a custom license or template + # Asset directories are .reuse and LICENSES + make_asset_dirs(tmp_path, template_path, template_name, license_path, license_name) + + # Set up git repository in tmp_path + git.Repo.init(tmp_path) + repo = git.Repo(tmp_path) + repo.index.commit("initialized git repo for tmp_path") + + # Copy file that will cause an IndexError to git repository + shutil.copyfile( + os.path.join(REPO_PATH, "tests", "test_reuse_files", test_filename), test_filename + ) + + custom_args = [ + test_filename, + "--custom_template=copyright_only", + "--custom_copyright=ANSYS, Inc. Unauthorized use, distribution, \ + or duplication is prohibited.", + "--ignore_license_check", + "--start_year=2023", + ] + + # Assert the hook failed + assert add_argv_run(repo, test_filename, custom_args) == 1 + + # Assert the single line comment changed to + # a multiline comment. This causes an IndexError in the hook + file = open(test_filename, "r") + count = 0 + for line in file: + count += 1 + if count == 1: + assert "/*" in line + # Ensure header was updated correctly and didn't add + # an extra SPDX-Identifier line + if count == 2: + assert f" * Copyright (C) 2023 - {dt.today().year} ANSYS, Inc. Unauthorized use" in line + if count == 3: + assert "*/" in line + if count > 4: + break + file.close() + + os.chdir(REPO_PATH) diff --git a/tests/LICENSES/ECL-1.0.txt b/tests/test_reuse_files/LICENSES/ECL-1.0.txt similarity index 100% rename from tests/LICENSES/ECL-1.0.txt rename to tests/test_reuse_files/LICENSES/ECL-1.0.txt diff --git a/tests/test_reuse_files/bad_chars.py b/tests/test_reuse_files/bad_chars.py new file mode 100644 index 00000000..319368ba --- /dev/null +++ b/tests/test_reuse_files/bad_chars.py @@ -0,0 +1,4 @@ +"""Test if fileinput.input() in add-license-headers decodes bad characters.""" +# This intentionally does not have a header, so we can test the +# license header is added in test_reuse.py - test_bad_chars() +print("ÞĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİIJĴ") diff --git a/tests/test_reuse_files/index_error.scss b/tests/test_reuse_files/index_error.scss new file mode 100644 index 00000000..ef3378b9 --- /dev/null +++ b/tests/test_reuse_files/index_error.scss @@ -0,0 +1,2 @@ +// Copyright (C) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. +// This tests the before_hook index exception in add-license-headers. \ No newline at end of file diff --git a/tests/templates/copyright_only.jinja2 b/tests/test_reuse_files/templates/copyright_only.jinja2 similarity index 100% rename from tests/templates/copyright_only.jinja2 rename to tests/test_reuse_files/templates/copyright_only.jinja2 diff --git a/tests/templates/test_template.jinja2 b/tests/test_reuse_files/templates/test_template.jinja2 similarity index 100% rename from tests/templates/test_template.jinja2 rename to tests/test_reuse_files/templates/test_template.jinja2