Repository: scikit-build/scikit-build-core
Pull request #854 (2024-08-06 23:15:21)

Fix crash when building inside a submodule
If .git is a file instead of a directory (such as when you are building in a submodule), scikit-build-core will currently crash because a NotADirectoryError is thrown instead of a FileNotFoundError. This PR accounts for the resulting exception (NotADirectoryError) instead of crashing.

Another option instead of hardcoding this list would be to catch the more general `OSError`. However, I did not do that, because I am worried about silently swallowing too broad of a class of exceptions.

I wrote a unit test and also tested the fix [against the project][1] where I encountered this.

[1]: https://github.com/scikit-build/scikit-build-core/blob/main/.github/CONTRIBUTING.md#testing-a-project-with-a-branch--main

(By the way, this project is very helpful, thank you. The fact that I encountered this issue only a couple of hours after the release of 0.10.0 tells you how often I use it!)

henryiii: I think the error here actually depends on the operating system. https://github.com/python/cpython/issues/119993 PermissionsError is another one.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/scikit-build/scikit-build-core/pull/854?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scikit-build) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 83.86%. Comparing base [(`b6ad498`)](https://app.codecov.io/gh/scikit-build/scikit-build-core/commit/b6ad49802bff1956e182edf8128e1a2ef5f89918?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scikit-build) to head [(`858e5aa`)](https://app.codecov.io/gh/scikit-build/scikit-build-core/commit/858e5aac13b0bdda027f59cb36525a9ce56c7989?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scikit-build).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #854      +/-   ##
==========================================
- Coverage   83.87%   83.86%   -0.02%     
==========================================
  Files          74       74              
  Lines        4361     4362       +1     
==========================================
  Hits         3658     3658              
- Misses        703      704       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/scikit-build/scikit-build-core/pull/854?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scikit-build).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scikit-build).


Patch:
diff --git a/src/scikit_build_core/build/_file_processor.py b/src/scikit_build_core/build/_file_processor.py
index c396718..457f91c 100644
--- a/src/scikit_build_core/build/_file_processor.py
+++ b/src/scikit_build_core/build/_file_processor.py
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
 __all__ = ["each_unignored_file"]
 
 EXCLUDE_LINES = [
-    ".git/",
+    ".git",
     ".tox/",
     ".nox/",
     ".egg-info/",
@@ -39,7 +39,8 @@ def each_unignored_file(
     exclude_lines = EXCLUDE_LINES + list(exclude)
 
     for gi in [Path(".git/info/exclude"), Path(".gitignore")]:
-        with contextlib.suppress(FileNotFoundError), gi.open(encoding="utf-8") as f:
+        ignore_errs = [FileNotFoundError, NotADirectoryError]
+        with contextlib.suppress(*ignore_errs), gi.open(encoding="utf-8") as f:
             exclude_lines += f.readlines()
 
     nested_excludes = {


Test patch:
diff --git a/tests/test_file_processor.py b/tests/test_file_processor.py
index f1f993e..1ce36f2 100644
--- a/tests/test_file_processor.py
+++ b/tests/test_file_processor.py
@@ -65,3 +65,16 @@ def test_on_each_with_symlink(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -
         nested_dir / "not_ignored.txt",
         nested_dir / ".gitignore",
     }
+
+
+def test_dot_git_is_a_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
+    """
+    Test that each_unignored_file() does not crash when .git is a file (e.g.,
+    if the build is being run in a submodule)
+    """
+    monkeypatch.chdir(tmp_path)
+    # Create a file named .git
+    git = Path(".git")
+    git.write_text("gitdir: ../../.git/modules/foo")
+    # If this throws an exception, the test will fail
+    assert list(each_unignored_file(Path())) == []