python-package-folder 5.1.0__tar.gz → 5.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/PKG-INFO +1 -1
  2. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/coverage.svg +2 -2
  3. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/pyproject.toml +1 -3
  4. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/subfolder_build.py +152 -13
  5. python_package_folder-5.1.0/src/python_package_folder/_hatch_build.py +0 -173
  6. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.copier-answers.yml +0 -0
  7. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
  8. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
  9. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.cursor/rules/general.mdc +0 -0
  10. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.cursor/rules/python.mdc +0 -0
  11. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.github/workflows/ci.yml +0 -0
  12. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.github/workflows/publish.yml +0 -0
  13. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.gitignore +0 -0
  14. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/.vscode/settings.json +0 -0
  15. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/LICENSE +0 -0
  16. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/MANIFEST.in +0 -0
  17. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/Makefile +0 -0
  18. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/README.md +0 -0
  19. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/development.md +0 -0
  20. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/DEVELOPMENT.md +0 -0
  21. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/INSTALLATION.md +0 -0
  22. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/PUBLISHING.md +0 -0
  23. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/REFERENCE.md +0 -0
  24. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/USAGE.md +0 -0
  25. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/docs/VERSION_RESOLUTION.md +0 -0
  26. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/installation.md +0 -0
  27. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/publishing.md +0 -0
  28. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/__init__.py +0 -0
  29. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/__main__.py +0 -0
  30. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/analyzer.py +0 -0
  31. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/finder.py +0 -0
  32. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/manager.py +0 -0
  33. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/publisher.py +0 -0
  34. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/py.typed +0 -0
  35. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/python_package_folder.py +0 -0
  36. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/types.py +0 -0
  37. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/utils.py +0 -0
  38. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/version.py +0 -0
  39. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/src/python_package_folder/version_calculator.py +0 -0
  40. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/conftest.py +0 -0
  41. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/some_globals.py +0 -0
  42. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/subfolder_to_build/README.md +0 -0
  43. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
  44. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
  45. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
  46. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
  47. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/folder_structure/utility_folder/some_utility.py +0 -0
  48. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_build_with_external_deps.py +0 -0
  49. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_exclude_patterns.py +0 -0
  50. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_linting.py +0 -0
  51. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_preserve_directory_structure.py +0 -0
  52. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_publisher.py +0 -0
  53. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_shared_subdirectory_imports.py +0 -0
  54. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_spreadsheet_creation_imports.py +0 -0
  55. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_subfolder_build.py +0 -0
  56. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_third_party_dependencies.py +0 -0
  57. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_utils.py +0 -0
  58. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_version_calculator.py +0 -0
  59. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/test_version_manager.py +0 -0
  60. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/tests/tests.py +0 -0
  61. {python_package_folder-5.1.0 → python_package_folder-5.1.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-package-folder
3
- Version: 5.1.0
3
+ Version: 5.1.2
4
4
  Summary: Python package to automatically package and build a folder, fetching all relevant dependencies.
5
5
  Project-URL: Repository, https://github.com/alelom/python-package-folder
6
6
  Author-email: Alessio Lombardi <work@alelom.com>
@@ -14,7 +14,7 @@
14
14
  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
15
15
  <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
16
16
  <text x="31.5" y="14">coverage</text>
17
- <text x="81" y="15" fill="#010101" fill-opacity=".3">65%</text>
18
- <text x="81" y="14">65%</text>
17
+ <text x="81" y="15" fill="#010101" fill-opacity=".3">63%</text>
18
+ <text x="81" y="14">63%</text>
19
19
  </g>
20
20
  </svg>
@@ -43,7 +43,7 @@ dependencies = [
43
43
 
44
44
  # ---- Dev dependencies ----
45
45
 
46
- version = "5.1.0"
46
+ version = "5.1.2"
47
47
  [dependency-groups]
48
48
  dev = [
49
49
  "pytest>=8.3.5",
@@ -154,7 +154,5 @@ testpaths = [
154
154
  "src",
155
155
  "tests",
156
156
  ]
157
- # Ignore build hooks - they're not test files and require hatchling which isn't in test deps
158
- ignore = ["_hatch_build.py"]
159
157
  norecursedirs = []
160
158
  filterwarnings = []
@@ -10,6 +10,7 @@ from __future__ import annotations
10
10
  import re
11
11
  import shutil
12
12
  import sys
13
+ import tempfile
13
14
  from pathlib import Path
14
15
  from typing import TYPE_CHECKING
15
16
 
@@ -75,6 +76,8 @@ class SubfolderBuildConfig:
75
76
  self.temp_readme: Path | None = None
76
77
  self.original_readme_backup: Path | None = None
77
78
  self._used_subfolder_pyproject = False
79
+ self._excluded_files: list[tuple[Path, Path]] = [] # List of (original_path, temp_path) tuples
80
+ self._exclude_temp_dir: Path | None = None
78
81
 
79
82
  def _derive_package_name(self) -> str:
80
83
  """Derive package name from source directory name."""
@@ -323,6 +326,10 @@ class SubfolderBuildConfig:
323
326
  # Handle README file
324
327
  self._handle_readme()
325
328
 
329
+ # Exclude files matching exclude patterns
330
+ if exclude_patterns:
331
+ self._exclude_files_by_patterns(exclude_patterns)
332
+
326
333
  return original_pyproject
327
334
 
328
335
  # No pyproject.toml in subfolder, create one from parent
@@ -430,6 +437,10 @@ class SubfolderBuildConfig:
430
437
  # Handle README file
431
438
  self._handle_readme()
432
439
 
440
+ # Exclude files matching exclude patterns
441
+ if exclude_patterns:
442
+ self._exclude_files_by_patterns(exclude_patterns)
443
+
433
444
  return original_pyproject
434
445
 
435
446
  def _modify_pyproject_string(
@@ -590,18 +601,39 @@ class SubfolderBuildConfig:
590
601
  # Use only-include for source distributions to ensure only the subfolder is included
591
602
  # This prevents including files from the project root
592
603
  if package_dirs:
593
- result.append("")
594
- result.append("[tool.hatch.build.targets.sdist]")
595
- # Include only the subfolder directory and necessary files
596
- only_include_paths = [package_dirs[0]]
597
- # Also include pyproject.toml and README if they exist
598
- only_include_paths.append("pyproject.toml")
599
- only_include_paths.append("README.md")
600
- only_include_paths.append("README.rst")
601
- only_include_paths.append("README.txt")
602
- only_include_paths.append("README")
603
- only_include_str = ", ".join(f'"{p}"' for p in only_include_paths)
604
- result.append(f"only-include = [{only_include_str}]")
604
+ # Check if sdist section already exists
605
+ sdist_section_exists = any(
606
+ line.strip().startswith("[tool.hatch.build.targets.sdist]")
607
+ for line in result
608
+ )
609
+ # Check if only-include is already set in the sdist section
610
+ only_include_set = False
611
+ if sdist_section_exists:
612
+ in_sdist_section = False
613
+ for line in result:
614
+ if line.strip().startswith("[tool.hatch.build.targets.sdist]"):
615
+ in_sdist_section = True
616
+ elif line.strip().startswith("[") and in_sdist_section:
617
+ in_sdist_section = False
618
+ elif in_sdist_section and re.match(r"^\s*only-include\s*=", line):
619
+ only_include_set = True
620
+ break
621
+
622
+ if not sdist_section_exists or not only_include_set:
623
+ if not sdist_section_exists:
624
+ result.append("")
625
+ result.append("[tool.hatch.build.targets.sdist]")
626
+ # Include only the subfolder directory and necessary files
627
+ only_include_paths = [package_dirs[0]]
628
+ # Also include pyproject.toml and README if they exist
629
+ only_include_paths.append("pyproject.toml")
630
+ only_include_paths.append("README.md")
631
+ only_include_paths.append("README.rst")
632
+ only_include_paths.append("README.txt")
633
+ only_include_paths.append("README")
634
+ only_include_str = ", ".join(f'"{p}"' for p in only_include_paths)
635
+ if not only_include_set:
636
+ result.append(f"only-include = [{only_include_str}]")
605
637
 
606
638
  # Add dependency group if specified
607
639
  if dependency_group:
@@ -933,6 +965,110 @@ class SubfolderBuildConfig:
933
965
  target_readme.write_text(readme_content, encoding="utf-8")
934
966
  self.temp_readme = target_readme
935
967
 
968
+ def _exclude_files_by_patterns(self, exclude_patterns: list[str]) -> None:
969
+ """
970
+ Temporarily move files matching exclude patterns out of the source directory.
971
+
972
+ Files are moved to a temporary directory and will be restored in restore().
973
+ This ensures excluded files are not included in the build.
974
+
975
+ Args:
976
+ exclude_patterns: List of regex patterns to match against path components
977
+ """
978
+ if not exclude_patterns:
979
+ return
980
+
981
+ # Compile regex patterns for efficiency
982
+ compiled_patterns = [re.compile(pattern) for pattern in exclude_patterns]
983
+
984
+ def should_exclude(path: Path) -> bool:
985
+ """Check if a path should be excluded based on patterns."""
986
+ # Check each component of the path
987
+ for part in path.parts:
988
+ # Check if any part matches any pattern
989
+ for pattern in compiled_patterns:
990
+ if pattern.search(part):
991
+ return True
992
+ return False
993
+
994
+ # Create temporary directory for excluded files
995
+ if self._exclude_temp_dir is None:
996
+ self._exclude_temp_dir = Path(tempfile.mkdtemp(prefix="python-package-folder-excluded-"))
997
+
998
+ # Find all files and directories in src_dir that match exclude patterns
999
+ excluded_items: list[Path] = []
1000
+
1001
+ # Walk through src_dir and find matching items
1002
+ # Sort by depth (shallow first) so we can skip children of excluded directories
1003
+ all_items = sorted(self.src_dir.rglob("*"), key=lambda p: len(p.parts))
1004
+
1005
+ for item in all_items:
1006
+ # Skip if already excluded (parent was excluded)
1007
+ if any(
1008
+ excluded.is_dir() and (item == excluded or item.is_relative_to(excluded))
1009
+ for excluded in excluded_items
1010
+ ):
1011
+ continue
1012
+
1013
+ # Check if this item should be excluded
1014
+ try:
1015
+ rel_path = item.relative_to(self.src_dir)
1016
+ if should_exclude(rel_path):
1017
+ excluded_items.append(item)
1018
+ except ValueError:
1019
+ # Path is not relative to src_dir, skip
1020
+ continue
1021
+
1022
+ # Move excluded items to temporary directory
1023
+ for item in excluded_items:
1024
+ try:
1025
+ # Calculate relative path from src_dir
1026
+ rel_path = item.relative_to(self.src_dir)
1027
+ # Create corresponding path in temp directory
1028
+ temp_path = self._exclude_temp_dir / rel_path
1029
+ # Create parent directories if needed
1030
+ temp_path.parent.mkdir(parents=True, exist_ok=True)
1031
+
1032
+ # Move the item
1033
+ if item.exists():
1034
+ shutil.move(str(item), str(temp_path))
1035
+ self._excluded_files.append((item, temp_path))
1036
+ print(f"Excluded {rel_path} from build", file=sys.stderr)
1037
+ except Exception as e:
1038
+ print(
1039
+ f"Warning: Could not exclude {item}: {e}",
1040
+ file=sys.stderr,
1041
+ )
1042
+
1043
+ def _restore_excluded_files(self) -> None:
1044
+ """Restore files that were excluded by _exclude_files_by_patterns."""
1045
+ # Restore files in reverse order (to handle nested directories correctly)
1046
+ for original_path, temp_path in reversed(self._excluded_files):
1047
+ try:
1048
+ if temp_path.exists():
1049
+ # Ensure parent directory exists
1050
+ original_path.parent.mkdir(parents=True, exist_ok=True)
1051
+ # Move back
1052
+ shutil.move(str(temp_path), str(original_path))
1053
+ except Exception as e:
1054
+ print(
1055
+ f"Warning: Could not restore excluded file {original_path}: {e}",
1056
+ file=sys.stderr,
1057
+ )
1058
+
1059
+ # Clean up temporary directory
1060
+ if self._exclude_temp_dir and self._exclude_temp_dir.exists():
1061
+ try:
1062
+ shutil.rmtree(self._exclude_temp_dir)
1063
+ except Exception as e:
1064
+ print(
1065
+ f"Warning: Could not remove temporary exclude directory {self._exclude_temp_dir}: {e}",
1066
+ file=sys.stderr,
1067
+ )
1068
+
1069
+ self._excluded_files.clear()
1070
+ self._exclude_temp_dir = None
1071
+
936
1072
  def restore(self) -> None:
937
1073
  """
938
1074
  Restore the original pyproject.toml and remove temporary __init__.py if created.
@@ -942,6 +1078,9 @@ class SubfolderBuildConfig:
942
1078
  This method removes the temporary pyproject.toml and restores the original from
943
1079
  the backup location, ensuring the original file is never modified.
944
1080
  """
1081
+ # Restore excluded files first
1082
+ self._restore_excluded_files()
1083
+
945
1084
  # Remove temporary __init__.py if we created it
946
1085
  if self._temp_init_created:
947
1086
  init_file = self.src_dir / "__init__.py"
@@ -1011,4 +1150,4 @@ class SubfolderBuildConfig:
1011
1150
 
1012
1151
  def __exit__(self, exc_type, exc_val, exc_tb) -> None: # noqa: ARG002
1013
1152
  """Context manager exit - always restore."""
1014
- self.restore()
1153
+ self.restore()
@@ -1,173 +0,0 @@
1
- """
2
- Hatch build hook to automatically include all files from the scripts directory.
3
-
4
- This hook ensures all non-Python files in the scripts directory are included
5
- in the wheel without creating duplicates, and automatically includes any new
6
- files added to the directory without requiring manual configuration updates.
7
-
8
- Also filters files based on exclude patterns from pyproject.toml.
9
- """
10
-
11
- import re
12
- import sys
13
- from pathlib import Path
14
- from typing import Any
15
-
16
- from hatchling.builders.hooks.plugin.interface import BuildHookInterface
17
-
18
- from .utils import read_exclude_patterns
19
-
20
-
21
- class CustomBuildHook(BuildHookInterface):
22
- """Build hook to include all files from the scripts directory and filter exclude patterns."""
23
-
24
- def initialize(self, version: str, build_data: dict[str, Any]) -> None:
25
- """Initialize the build hook and add scripts directory files."""
26
- # Debug: Print to stderr so it shows in build output
27
- print(f"[DEBUG] Build hook called. Root: {self.root}", file=sys.stderr)
28
-
29
- # Read exclude patterns from pyproject.toml
30
- pyproject_path = Path(self.root) / "pyproject.toml"
31
- exclude_patterns = read_exclude_patterns(pyproject_path)
32
-
33
- if exclude_patterns:
34
- print(f"[DEBUG] Found {len(exclude_patterns)} exclude pattern(s): {exclude_patterns}", file=sys.stderr)
35
- # Filter build_data entries based on exclude patterns
36
- self._filter_build_data(build_data, exclude_patterns)
37
-
38
- # Try multiple possible locations for the scripts directory
39
- # 1. Source layout: src/python_package_folder/scripts
40
- # 2. Sdist layout: python_package_folder/scripts (after extraction)
41
- # 3. Alternative sdist layout: scripts/ (if extracted differently)
42
- possible_scripts_dirs = [
43
- Path(self.root) / "src" / "python_package_folder" / "scripts",
44
- Path(self.root) / "python_package_folder" / "scripts",
45
- Path(self.root) / "scripts",
46
- ]
47
-
48
- scripts_dir = None
49
- for possible_dir in possible_scripts_dirs:
50
- if possible_dir.exists() and possible_dir.is_dir():
51
- scripts_dir = possible_dir
52
- print(f"[DEBUG] Found scripts dir at: {scripts_dir}", file=sys.stderr)
53
- break
54
-
55
- if scripts_dir is None:
56
- print(f"[DEBUG] Scripts directory not found. Tried: {[str(d) for d in possible_scripts_dirs]}", file=sys.stderr)
57
- return
58
-
59
- # If scripts directory exists, include all files from it
60
- if scripts_dir.exists() and scripts_dir.is_dir():
61
- # Add all files from scripts directory to force-include
62
- # This ensures they're included in the wheel at the correct location
63
- for script_file in scripts_dir.iterdir():
64
- if script_file.is_file():
65
- # Calculate relative paths from project root
66
- try:
67
- source_path = script_file.relative_to(self.root)
68
- except ValueError:
69
- # If relative_to fails, try to construct path manually
70
- # This can happen with sdist layouts
71
- if "python_package_folder" in str(script_file):
72
- # Extract the part after python_package_folder
73
- parts = script_file.parts
74
- try:
75
- idx = parts.index("python_package_folder")
76
- source_path = Path(*parts[idx:])
77
- except (ValueError, IndexError):
78
- # Fallback: use the filename
79
- source_path = Path("python_package_folder") / "scripts" / script_file.name
80
- else:
81
- source_path = Path("python_package_folder") / "scripts" / script_file.name
82
-
83
- # Target path inside the wheel package (always the same)
84
- target_path = f"python_package_folder/scripts/{script_file.name}"
85
-
86
- print(f"[DEBUG] Adding {source_path} -> {target_path}", file=sys.stderr)
87
-
88
- # Add to force-include (hatchling will handle this)
89
- # We need to add it to build_data['force_include']
90
- if "force_include" not in build_data:
91
- build_data["force_include"] = {}
92
- build_data["force_include"][str(source_path)] = target_path
93
-
94
- print(f"[DEBUG] force_include now has {len(build_data.get('force_include', {}))} entries", file=sys.stderr)
95
-
96
- def _filter_build_data(self, build_data: dict[str, Any], exclude_patterns: list[str]) -> None:
97
- """
98
- Filter build_data entries based on exclude patterns.
99
-
100
- Removes files/directories that match any of the exclude patterns from
101
- build_data. Patterns are matched against any path component using regex.
102
-
103
- Args:
104
- build_data: Hatchling build data dictionary
105
- exclude_patterns: List of regex patterns to match against paths
106
- """
107
- # Compile regex patterns for efficiency
108
- compiled_patterns = [re.compile(pattern) for pattern in exclude_patterns]
109
-
110
- def should_exclude(path_str: str) -> bool:
111
- """Check if a path should be excluded based on patterns."""
112
- # Check each component of the path
113
- path = Path(path_str)
114
- for part in path.parts:
115
- # Check if any part matches any pattern
116
- for pattern in compiled_patterns:
117
- if pattern.search(part):
118
- return True
119
- return False
120
-
121
- # Filter force_include entries
122
- if "force_include" in build_data and isinstance(build_data["force_include"], dict):
123
- original_count = len(build_data["force_include"])
124
- filtered = {
125
- source: target
126
- for source, target in build_data["force_include"].items()
127
- if not should_exclude(source) and not should_exclude(target)
128
- }
129
- build_data["force_include"] = filtered
130
- excluded_count = original_count - len(filtered)
131
- if excluded_count > 0:
132
- print(
133
- f"[DEBUG] Excluded {excluded_count} file(s) from force_include based on exclude patterns",
134
- file=sys.stderr,
135
- )
136
-
137
- # Filter other file collections that might exist
138
- # Hatchling may store files in different keys depending on the build target
139
- for key in ["shared_data", "artifacts"]:
140
- if key in build_data and isinstance(build_data[key], dict):
141
- original_count = len(build_data[key])
142
- filtered = {
143
- source: target
144
- for source, target in build_data[key].items()
145
- if not should_exclude(source) and not should_exclude(target)
146
- }
147
- build_data[key] = filtered
148
- excluded_count = original_count - len(filtered)
149
- if excluded_count > 0:
150
- print(
151
- f"[DEBUG] Excluded {excluded_count} file(s) from {key} based on exclude patterns",
152
- file=sys.stderr,
153
- )
154
-
155
- # Filter files list if it exists (for sdist)
156
- if "files" in build_data and isinstance(build_data["files"], list):
157
- original_count = len(build_data["files"])
158
- filtered = [
159
- file_entry
160
- for file_entry in build_data["files"]
161
- if not should_exclude(str(file_entry))
162
- ]
163
- build_data["files"] = filtered
164
- excluded_count = original_count - len(filtered)
165
- if excluded_count > 0:
166
- print(
167
- f"[DEBUG] Excluded {excluded_count} file(s) from files list based on exclude patterns",
168
- file=sys.stderr,
169
- )
170
-
171
-
172
- # Export the hook class (hatchling might need this)
173
- __all__ = ["CustomBuildHook"]