diff --git a/integration_tests/base_test.py b/integration_tests/base_test.py index 21ed2e0f..b41326d3 100644 --- a/integration_tests/base_test.py +++ b/integration_tests/base_test.py @@ -67,6 +67,10 @@ def setup_method(self): "You must register the codemod to a CodemodCollection." ) from exc + @property + def relative_code_path(self): + return os.path.relpath(self.code_path, SAMPLES_DIR) + def _assert_run_fields(self, run, output_path): assert run["vendor"] == "pixee" assert run["tool"] == "codemodder-python" @@ -74,12 +78,12 @@ def _assert_run_fields(self, run, output_path): assert run["elapsed"] != "" assert ( run["commandLine"] - == f"codemodder {SAMPLES_DIR} --output {output_path} --codemod-include={self.codemod_wrapper.name} --path-include={self.code_path}" + == f"codemodder {SAMPLES_DIR} --output {output_path} --codemod-include={self.codemod_wrapper.name} --path-include={self.relative_code_path}" ) assert run["directory"] == os.path.abspath(SAMPLES_DIR) assert run["sarifs"] == [] - def _assert_results_fields(self, results, output_path): + def _assert_results_fields(self, results): assert len(results) == 1 result = results[0] assert result["codemod"] == self.codemod_wrapper.id @@ -94,9 +98,11 @@ def _assert_results_fields(self, results, output_path): # A codemod may change multiple files. For now we will # assert the resulting data for one file only. change = [ - result for result in result["changeset"] if result["path"] == output_path + result + for result in result["changeset"] + if result["path"] == self.relative_code_path ][0] - assert change["path"] == output_path + assert change["path"] == self.relative_code_path assert change["diff"] == self.expected_diff assert len(change["changes"]) == self.num_changes @@ -114,10 +120,7 @@ def _assert_codetf_output(self): run = codetf["run"] self._assert_run_fields(run, self.output_path) results = codetf["results"] - # CodeTf2 spec requires relative paths - self._assert_results_fields( - results, os.path.relpath(self.code_path, SAMPLES_DIR) - ) + self._assert_results_fields(results) def check_code_before(self): with open(self.code_path, "r", encoding="utf-8") as f: @@ -145,7 +148,7 @@ def test_file_rewritten(self): "--output", self.output_path, f"--codemod-include={self.codemod_wrapper.name}", - f"--path-include={self.code_path}", + f"--path-include={self.relative_code_path}", ] self.check_code_before() diff --git a/src/codemodder/code_directory.py b/src/codemodder/code_directory.py index b69ff655..d0a06cc5 100644 --- a/src/codemodder/code_directory.py +++ b/src/codemodder/code_directory.py @@ -49,10 +49,13 @@ def filter_files( else [x for x in (patterns or []) if ":" not in x] ) - # Filter patterns should be relative to a parent_path, not absolute. # TODO: handle case when parent path is "." - patterns = [f"{parent_path}{x}" for x in patterns] - + patterns = [ + str(Path(parent_path) / Path(pat)) + if not pat.startswith("*") + else parent_path + pat + for pat in patterns + ] return itertools.chain(*[fnmatch.filter(names, pattern) for pattern in patterns]) diff --git a/src/codemodder/codemodder.py b/src/codemodder/codemodder.py index e697848c..5eed3680 100644 --- a/src/codemodder/codemodder.py +++ b/src/codemodder/codemodder.py @@ -3,7 +3,6 @@ import logging import os import sys -from textwrap import indent import libcst as cst from libcst.codemod import CodemodContext diff --git a/src/codemodder/semgrep.py b/src/codemodder/semgrep.py index ad3e93ee..78f6297e 100644 --- a/src/codemodder/semgrep.py +++ b/src/codemodder/semgrep.py @@ -37,7 +37,7 @@ def run(execution_context: CodemodExecutionContext, yaml_files: List[Path]) -> d ) ) if execution_context.path_include: - # I don't know why but passing the parent path to include patterns has unexpected behavior + # Note: parent path is not passed with --include command.extend( itertools.chain.from_iterable( map(lambda f: ["--include", str(f)], execution_context.path_include) diff --git a/tests/test_code_directory.py b/tests/test_code_directory.py index 7a980804..debc55b1 100644 --- a/tests/test_code_directory.py +++ b/tests/test_code_directory.py @@ -138,3 +138,11 @@ def test_include_test_without_default_excludes(self, dir_structure): def test_extract_line_from_pattern(self): lines = file_line_patterns(Path("insecure_random.py"), ["insecure_*.py:3"]) assert lines == [3] + + def test_include_specific_file(self, dir_structure): + expected = ["empty_for_testing.py"] + files = match_files( + dir_structure / "samples" / "more_samples", + include_paths=["empty_for_testing.py"], + ) + self._assert_expected(files, expected)