python-package-folder 5.0.1__tar.gz → 5.1.1__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.
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/PKG-INFO +1 -1
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/coverage.svg +2 -2
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/REFERENCE.md +23 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/USAGE.md +10 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/pyproject.toml +1 -1
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/_hatch_build.py +90 -1
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/subfolder_build.py +275 -6
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/utils.py +69 -0
- python_package_folder-5.1.1/tests/test_exclude_patterns.py +186 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_subfolder_build.py +3 -3
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.copier-answers.yml +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.github/workflows/ci.yml +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.github/workflows/publish.yml +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.gitignore +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/.vscode/settings.json +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/LICENSE +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/MANIFEST.in +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/Makefile +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/README.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/development.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/DEVELOPMENT.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/INSTALLATION.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/PUBLISHING.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/docs/VERSION_RESOLUTION.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/installation.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/publishing.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/types.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/version.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/version_calculator.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/conftest.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_linting.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_publisher.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_utils.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_version_calculator.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_version_manager.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/tests.py +0 -0
- {python_package_folder-5.0.1 → python_package_folder-5.1.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.1.1
|
|
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">
|
|
18
|
-
<text x="81" y="14">
|
|
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>
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
+
## Configuration
|
|
4
|
+
|
|
5
|
+
### Exclude Patterns
|
|
6
|
+
|
|
7
|
+
You can configure exclude patterns in `pyproject.toml` to prevent folders and files from being included in published packages (wheel/sdist). Patterns are matched using regex against any path component (directory or file name).
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
[tool.python-package-folder]
|
|
11
|
+
exclude-patterns = ["_SS", "__SS", ".*_test.*", "sandbox"]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Pattern Matching:**
|
|
15
|
+
- Patterns are regex strings that match against any path component
|
|
16
|
+
- If any component in a path matches any pattern, the entire path is excluded
|
|
17
|
+
- Examples:
|
|
18
|
+
- `"_SS"` - Matches any path component containing `_SS` (e.g., `data_storage/_SS/...`)
|
|
19
|
+
- `".*_test.*"` - Matches any path component containing `_test` (e.g., `my_test_file.py`, `test_data/`)
|
|
20
|
+
- `"sandbox"` - Matches any path component containing `sandbox`
|
|
21
|
+
|
|
22
|
+
**Subfolder Builds:**
|
|
23
|
+
- Exclude patterns from the root `pyproject.toml` are automatically applied to subfolder builds
|
|
24
|
+
- Patterns are injected into the temporary `pyproject.toml` created for subfolder builds
|
|
25
|
+
|
|
3
26
|
## Command Line Options
|
|
4
27
|
|
|
5
28
|
```
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
- Subfolder-specific package name (derived or custom)
|
|
47
47
|
- Specified version
|
|
48
48
|
- Correct package path for hatchling
|
|
49
|
+
- Exclude patterns from root `pyproject.toml` (if configured)
|
|
49
50
|
6. **Build Execution**: Runs build command with all dependencies in place
|
|
50
51
|
7. **Cleanup**: Restores original `pyproject.toml` and removes temporary `__init__.py`
|
|
51
52
|
|
|
@@ -114,6 +115,15 @@ python-package-folder --version "1.0.0" --dependency-group "dev" --publish pypi
|
|
|
114
115
|
|
|
115
116
|
The specified dependency group will be copied from the parent `pyproject.toml`'s `[dependency-groups]` section into the temporary `pyproject.toml` used for the subfolder build.
|
|
116
117
|
|
|
118
|
+
**Exclude Patterns**: You can configure exclude patterns in the root `pyproject.toml` to prevent certain folders/files from being included in published packages:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[tool.python-package-folder]
|
|
122
|
+
exclude-patterns = ["_SS", "__SS", ".*_test.*"]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
These patterns are automatically applied to both main package builds and subfolder builds. Patterns use regex matching against any path component (directory or file name). See the [API Reference](REFERENCE.md#exclude-patterns) for more details.
|
|
126
|
+
|
|
117
127
|
## Python API Usage
|
|
118
128
|
|
|
119
129
|
You can also use the package programmatically:
|
|
@@ -4,23 +4,37 @@ Hatch build hook to automatically include all files from the scripts directory.
|
|
|
4
4
|
This hook ensures all non-Python files in the scripts directory are included
|
|
5
5
|
in the wheel without creating duplicates, and automatically includes any new
|
|
6
6
|
files added to the directory without requiring manual configuration updates.
|
|
7
|
+
|
|
8
|
+
Also filters files based on exclude patterns from pyproject.toml.
|
|
7
9
|
"""
|
|
8
10
|
|
|
11
|
+
import re
|
|
9
12
|
import sys
|
|
10
13
|
from pathlib import Path
|
|
11
14
|
from typing import Any
|
|
12
15
|
|
|
13
16
|
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
14
17
|
|
|
18
|
+
from .utils import read_exclude_patterns
|
|
19
|
+
|
|
15
20
|
|
|
16
21
|
class CustomBuildHook(BuildHookInterface):
|
|
17
|
-
"""Build hook to include all files from the scripts directory."""
|
|
22
|
+
"""Build hook to include all files from the scripts directory and filter exclude patterns."""
|
|
18
23
|
|
|
19
24
|
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
|
|
20
25
|
"""Initialize the build hook and add scripts directory files."""
|
|
21
26
|
# Debug: Print to stderr so it shows in build output
|
|
22
27
|
print(f"[DEBUG] Build hook called. Root: {self.root}", file=sys.stderr)
|
|
23
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
|
+
|
|
24
38
|
# Try multiple possible locations for the scripts directory
|
|
25
39
|
# 1. Source layout: src/python_package_folder/scripts
|
|
26
40
|
# 2. Sdist layout: python_package_folder/scripts (after extraction)
|
|
@@ -79,6 +93,81 @@ class CustomBuildHook(BuildHookInterface):
|
|
|
79
93
|
|
|
80
94
|
print(f"[DEBUG] force_include now has {len(build_data.get('force_include', {}))} entries", file=sys.stderr)
|
|
81
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
|
+
|
|
82
171
|
|
|
83
172
|
# Export the hook class (hatchling might need this)
|
|
84
173
|
__all__ = ["CustomBuildHook"]
|
|
@@ -24,6 +24,8 @@ except ImportError:
|
|
|
24
24
|
except ImportError:
|
|
25
25
|
tomllib = None
|
|
26
26
|
|
|
27
|
+
from .utils import read_exclude_patterns
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
class SubfolderBuildConfig:
|
|
29
31
|
"""
|
|
@@ -203,13 +205,80 @@ class SubfolderBuildConfig:
|
|
|
203
205
|
has_build_system = any(line.strip().startswith("[build-system]") for line in result)
|
|
204
206
|
if not has_build_system:
|
|
205
207
|
# Insert build-system at the very beginning of the file
|
|
208
|
+
# Include python-package-folder in requires so the build hook is available
|
|
206
209
|
build_system_lines = [
|
|
207
210
|
"[build-system]",
|
|
208
|
-
'requires = ["hatchling"]',
|
|
211
|
+
'requires = ["hatchling", "python-package-folder"]',
|
|
209
212
|
'build-backend = "hatchling.build"',
|
|
210
213
|
"",
|
|
211
214
|
]
|
|
212
215
|
result = build_system_lines + result
|
|
216
|
+
else:
|
|
217
|
+
# Ensure python-package-folder is in requires for build hook availability
|
|
218
|
+
in_build_system = False
|
|
219
|
+
for i, line in enumerate(result):
|
|
220
|
+
if line.strip().startswith("[build-system]"):
|
|
221
|
+
in_build_system = True
|
|
222
|
+
elif in_build_system and line.strip().startswith("requires"):
|
|
223
|
+
# Check if python-package-folder is already in requires
|
|
224
|
+
if "python-package-folder" not in line:
|
|
225
|
+
# Add python-package-folder to requires
|
|
226
|
+
if "]" in line:
|
|
227
|
+
# Single-line requires
|
|
228
|
+
result[i] = line.rstrip().rstrip("]") + ', "python-package-folder"]'
|
|
229
|
+
else:
|
|
230
|
+
# Multi-line requires, find the closing bracket
|
|
231
|
+
for j in range(i + 1, len(result)):
|
|
232
|
+
if "]" in result[j]:
|
|
233
|
+
# Insert before closing
|
|
234
|
+
# Determine indentation from the previous line (last item in list)
|
|
235
|
+
if j > i + 1:
|
|
236
|
+
# Look at the previous line to get indentation
|
|
237
|
+
prev_line = result[j - 1]
|
|
238
|
+
# Extract leading whitespace from previous line
|
|
239
|
+
indent = len(prev_line) - len(prev_line.lstrip())
|
|
240
|
+
indent_str = " " * indent
|
|
241
|
+
else:
|
|
242
|
+
# Fallback: use standard 4-space indent
|
|
243
|
+
indent_str = " "
|
|
244
|
+
result.insert(j, f'{indent_str}"python-package-folder",')
|
|
245
|
+
break
|
|
246
|
+
break
|
|
247
|
+
elif in_build_system and line.strip().startswith("[") and not line.strip().startswith("[build-system"):
|
|
248
|
+
# End of build-system section
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
# Register the build hook to enable exclude patterns
|
|
252
|
+
build_hook_registered = any(
|
|
253
|
+
"[tool.hatch.build.hooks.custom]" in line for line in result
|
|
254
|
+
)
|
|
255
|
+
if not build_hook_registered:
|
|
256
|
+
# Add build hook registration after [tool.hatch.build.targets.wheel] section
|
|
257
|
+
hook_insert_index = len(result)
|
|
258
|
+
for i, line in enumerate(result):
|
|
259
|
+
if line.strip().startswith("[tool.hatch.build.targets.wheel]"):
|
|
260
|
+
# Find the end of this section
|
|
261
|
+
for j in range(i + 1, len(result)):
|
|
262
|
+
if result[j].strip().startswith("[") and not result[j].strip().startswith(
|
|
263
|
+
"[tool.hatch.build.targets"
|
|
264
|
+
):
|
|
265
|
+
hook_insert_index = j
|
|
266
|
+
break
|
|
267
|
+
if hook_insert_index == len(result):
|
|
268
|
+
# Insert after packages line if section continues
|
|
269
|
+
for j in range(i + 1, len(result)):
|
|
270
|
+
if result[j].strip().startswith("["):
|
|
271
|
+
hook_insert_index = j
|
|
272
|
+
break
|
|
273
|
+
break
|
|
274
|
+
|
|
275
|
+
# Insert build hook registration
|
|
276
|
+
hook_lines = [
|
|
277
|
+
"",
|
|
278
|
+
"[tool.hatch.build.hooks.custom]",
|
|
279
|
+
'path = "python_package_folder._hatch_build:CustomBuildHook"',
|
|
280
|
+
]
|
|
281
|
+
result[hook_insert_index:hook_insert_index] = hook_lines
|
|
213
282
|
|
|
214
283
|
# Ensure hatch build section exists if packages path is needed
|
|
215
284
|
if not packages_set and correct_packages_path:
|
|
@@ -288,6 +357,11 @@ class SubfolderBuildConfig:
|
|
|
288
357
|
subfolder_content = subfolder_pyproject.read_text(encoding="utf-8")
|
|
289
358
|
# Adjust packages path to be relative to project root
|
|
290
359
|
adjusted_content = self._adjust_subfolder_pyproject_packages_path(subfolder_content)
|
|
360
|
+
|
|
361
|
+
# Read exclude patterns from root pyproject.toml and inject them
|
|
362
|
+
exclude_patterns = read_exclude_patterns(original_pyproject)
|
|
363
|
+
if exclude_patterns:
|
|
364
|
+
adjusted_content = self._inject_exclude_patterns(adjusted_content, exclude_patterns)
|
|
291
365
|
|
|
292
366
|
# Write adjusted content to temporary file
|
|
293
367
|
temp_pyproject_path.write_text(adjusted_content, encoding="utf-8")
|
|
@@ -373,6 +447,9 @@ class SubfolderBuildConfig:
|
|
|
373
447
|
file=sys.stderr,
|
|
374
448
|
)
|
|
375
449
|
|
|
450
|
+
# Read exclude patterns from root pyproject.toml
|
|
451
|
+
exclude_patterns = read_exclude_patterns(original_pyproject)
|
|
452
|
+
|
|
376
453
|
if data:
|
|
377
454
|
# Modify using parsed data
|
|
378
455
|
if "project" in data:
|
|
@@ -394,12 +471,12 @@ class SubfolderBuildConfig:
|
|
|
394
471
|
|
|
395
472
|
# For now, use string manipulation (tomli-w not in stdlib)
|
|
396
473
|
modified_content = self._modify_pyproject_string(
|
|
397
|
-
original_content, parent_dependency_group
|
|
474
|
+
original_content, parent_dependency_group, exclude_patterns
|
|
398
475
|
)
|
|
399
476
|
else:
|
|
400
477
|
# Use string manipulation
|
|
401
478
|
modified_content = self._modify_pyproject_string(
|
|
402
|
-
original_content, parent_dependency_group
|
|
479
|
+
original_content, parent_dependency_group, exclude_patterns
|
|
403
480
|
)
|
|
404
481
|
|
|
405
482
|
# Write the modified content to a temporary file
|
|
@@ -423,7 +500,10 @@ class SubfolderBuildConfig:
|
|
|
423
500
|
return original_pyproject
|
|
424
501
|
|
|
425
502
|
def _modify_pyproject_string(
|
|
426
|
-
self,
|
|
503
|
+
self,
|
|
504
|
+
content: str,
|
|
505
|
+
dependency_group: dict[str, list[str]] | None = None,
|
|
506
|
+
exclude_patterns: list[str] | None = None,
|
|
427
507
|
) -> str:
|
|
428
508
|
"""Modify pyproject.toml content using string manipulation."""
|
|
429
509
|
lines = content.split("\n")
|
|
@@ -557,13 +637,54 @@ class SubfolderBuildConfig:
|
|
|
557
637
|
has_build_system = any(line.strip().startswith("[build-system]") for line in result)
|
|
558
638
|
if not has_build_system:
|
|
559
639
|
# Insert build-system at the very beginning of the file
|
|
640
|
+
# Include python-package-folder in requires so the build hook is available
|
|
560
641
|
build_system_lines = [
|
|
561
642
|
"[build-system]",
|
|
562
|
-
'requires = ["hatchling"]',
|
|
643
|
+
'requires = ["hatchling", "python-package-folder"]',
|
|
563
644
|
'build-backend = "hatchling.build"',
|
|
564
645
|
"",
|
|
565
646
|
]
|
|
566
647
|
result = build_system_lines + result
|
|
648
|
+
else:
|
|
649
|
+
# Ensure python-package-folder is in requires for build hook availability
|
|
650
|
+
in_build_system = False
|
|
651
|
+
requires_modified = False
|
|
652
|
+
for i, line in enumerate(result):
|
|
653
|
+
if line.strip().startswith("[build-system]"):
|
|
654
|
+
in_build_system = True
|
|
655
|
+
elif in_build_system and line.strip().startswith("requires"):
|
|
656
|
+
# Check if python-package-folder is already in requires
|
|
657
|
+
if "python-package-folder" not in line:
|
|
658
|
+
# Add python-package-folder to requires
|
|
659
|
+
if "]" in line:
|
|
660
|
+
# Single-line requires (may have closing bracket on same line)
|
|
661
|
+
if line.strip().endswith("]"):
|
|
662
|
+
result[i] = line.rstrip().rstrip("]") + ', "python-package-folder"]'
|
|
663
|
+
else:
|
|
664
|
+
# Closing bracket might be on same line but not at end
|
|
665
|
+
result[i] = line.rstrip().rstrip("]") + ', "python-package-folder"]'
|
|
666
|
+
else:
|
|
667
|
+
# Multi-line requires, find the closing bracket
|
|
668
|
+
for j in range(i + 1, len(result)):
|
|
669
|
+
if "]" in result[j]:
|
|
670
|
+
# Insert before closing
|
|
671
|
+
# Determine indentation from the previous line (last item in list)
|
|
672
|
+
if j > i + 1:
|
|
673
|
+
# Look at the previous line to get indentation
|
|
674
|
+
prev_line = result[j - 1]
|
|
675
|
+
# Extract leading whitespace from previous line
|
|
676
|
+
indent = len(prev_line) - len(prev_line.lstrip())
|
|
677
|
+
indent_str = " " * indent
|
|
678
|
+
else:
|
|
679
|
+
# Fallback: use standard 4-space indent
|
|
680
|
+
indent_str = " "
|
|
681
|
+
result.insert(j, f'{indent_str}"python-package-folder",')
|
|
682
|
+
break
|
|
683
|
+
requires_modified = True
|
|
684
|
+
break
|
|
685
|
+
elif in_build_system and line.strip().startswith("[") and not line.strip().startswith("[build-system"):
|
|
686
|
+
# End of build-system section
|
|
687
|
+
break
|
|
567
688
|
|
|
568
689
|
# Ensure packages is always set for subfolder builds
|
|
569
690
|
if not packages_set and package_dirs:
|
|
@@ -574,6 +695,50 @@ class SubfolderBuildConfig:
|
|
|
574
695
|
packages_str = ", ".join(f'"{p}"' for p in package_dirs)
|
|
575
696
|
result.append(f"packages = [{packages_str}]")
|
|
576
697
|
|
|
698
|
+
# Register the build hook to enable exclude patterns
|
|
699
|
+
# Always register the build hook if exclude patterns are present, or if we want to support them
|
|
700
|
+
# Check if build hook is already registered
|
|
701
|
+
build_hook_registered = any(
|
|
702
|
+
"[tool.hatch.build.hooks.custom]" in line for line in result
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
if not build_hook_registered:
|
|
706
|
+
# Add build hook registration after [tool.hatch.build.targets.wheel] section
|
|
707
|
+
hook_insert_index = len(result)
|
|
708
|
+
wheel_section_found = False
|
|
709
|
+
for i, line in enumerate(result):
|
|
710
|
+
if line.strip().startswith("[tool.hatch.build.targets.wheel]"):
|
|
711
|
+
wheel_section_found = True
|
|
712
|
+
# Find the end of this section
|
|
713
|
+
for j in range(i + 1, len(result)):
|
|
714
|
+
if result[j].strip().startswith("[") and not result[j].strip().startswith(
|
|
715
|
+
"[tool.hatch.build.targets"
|
|
716
|
+
):
|
|
717
|
+
hook_insert_index = j
|
|
718
|
+
break
|
|
719
|
+
if hook_insert_index == len(result):
|
|
720
|
+
# Insert after packages line if section continues
|
|
721
|
+
for j in range(i + 1, len(result)):
|
|
722
|
+
if result[j].strip().startswith("["):
|
|
723
|
+
hook_insert_index = j
|
|
724
|
+
break
|
|
725
|
+
break
|
|
726
|
+
|
|
727
|
+
# If wheel section not found, insert before sdist section or at end
|
|
728
|
+
if not wheel_section_found:
|
|
729
|
+
for i, line in enumerate(result):
|
|
730
|
+
if line.strip().startswith("[tool.hatch.build.targets.sdist]"):
|
|
731
|
+
hook_insert_index = i
|
|
732
|
+
break
|
|
733
|
+
|
|
734
|
+
# Insert build hook registration
|
|
735
|
+
hook_lines = [
|
|
736
|
+
"",
|
|
737
|
+
"[tool.hatch.build.hooks.custom]",
|
|
738
|
+
'path = "python_package_folder._hatch_build:CustomBuildHook"',
|
|
739
|
+
]
|
|
740
|
+
result[hook_insert_index:hook_insert_index] = hook_lines
|
|
741
|
+
|
|
577
742
|
# Use only-include for source distributions to ensure only the subfolder is included
|
|
578
743
|
# This prevents including files from the project root
|
|
579
744
|
if package_dirs:
|
|
@@ -635,8 +800,112 @@ class SubfolderBuildConfig:
|
|
|
635
800
|
dep_lines.append("]")
|
|
636
801
|
result[insert_index:insert_index] = dep_lines
|
|
637
802
|
|
|
803
|
+
# Add exclude patterns if specified
|
|
804
|
+
if exclude_patterns:
|
|
805
|
+
# Find where to insert [tool.python-package-folder] section
|
|
806
|
+
# Usually after [dependency-groups] or at the end
|
|
807
|
+
insert_index = len(result)
|
|
808
|
+
tool_section_exists = False
|
|
809
|
+
for i, line in enumerate(result):
|
|
810
|
+
if line.strip() == "[tool.python-package-folder]":
|
|
811
|
+
tool_section_exists = True
|
|
812
|
+
insert_index = i
|
|
813
|
+
break
|
|
814
|
+
elif line.strip().startswith("[tool.") and i > 0:
|
|
815
|
+
# Insert before other tool sections
|
|
816
|
+
insert_index = i
|
|
817
|
+
break
|
|
818
|
+
|
|
819
|
+
# Format exclude patterns
|
|
820
|
+
patterns_str = ", ".join(f'"{p}"' for p in exclude_patterns)
|
|
821
|
+
exclude_lines = [
|
|
822
|
+
"",
|
|
823
|
+
"[tool.python-package-folder]",
|
|
824
|
+
f'exclude-patterns = [{patterns_str}]',
|
|
825
|
+
]
|
|
826
|
+
|
|
827
|
+
if tool_section_exists:
|
|
828
|
+
# Replace or update existing section
|
|
829
|
+
end_index = insert_index + 1
|
|
830
|
+
while end_index < len(result) and not result[end_index].strip().startswith("["):
|
|
831
|
+
end_index += 1
|
|
832
|
+
# Check if exclude-patterns already exists
|
|
833
|
+
has_exclude_patterns = any(
|
|
834
|
+
"exclude-patterns" in result[i] for i in range(insert_index, end_index)
|
|
835
|
+
)
|
|
836
|
+
if has_exclude_patterns:
|
|
837
|
+
# Update existing exclude-patterns line
|
|
838
|
+
for i in range(insert_index, end_index):
|
|
839
|
+
if "exclude-patterns" in result[i]:
|
|
840
|
+
result[i] = f'exclude-patterns = [{patterns_str}]'
|
|
841
|
+
break
|
|
842
|
+
else:
|
|
843
|
+
# Add exclude-patterns to existing section
|
|
844
|
+
result.insert(end_index - 1, f'exclude-patterns = [{patterns_str}]')
|
|
845
|
+
else:
|
|
846
|
+
# Insert new section
|
|
847
|
+
result[insert_index:insert_index] = exclude_lines
|
|
848
|
+
|
|
638
849
|
return "\n".join(result)
|
|
639
850
|
|
|
851
|
+
def _inject_exclude_patterns(self, content: str, exclude_patterns: list[str]) -> str:
|
|
852
|
+
"""
|
|
853
|
+
Inject exclude patterns into pyproject.toml content.
|
|
854
|
+
|
|
855
|
+
Adds or updates [tool.python-package-folder] exclude-patterns section.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
content: pyproject.toml content
|
|
859
|
+
exclude_patterns: List of exclude patterns to inject
|
|
860
|
+
|
|
861
|
+
Returns:
|
|
862
|
+
Modified pyproject.toml content with exclude patterns
|
|
863
|
+
"""
|
|
864
|
+
if not exclude_patterns:
|
|
865
|
+
return content
|
|
866
|
+
|
|
867
|
+
lines = content.split("\n")
|
|
868
|
+
result = []
|
|
869
|
+
tool_section_exists = False
|
|
870
|
+
tool_section_index = -1
|
|
871
|
+
tool_section_end = -1
|
|
872
|
+
|
|
873
|
+
# Find [tool.python-package-folder] section
|
|
874
|
+
for i, line in enumerate(lines):
|
|
875
|
+
if line.strip() == "[tool.python-package-folder]":
|
|
876
|
+
tool_section_exists = True
|
|
877
|
+
tool_section_index = i
|
|
878
|
+
# Find end of section
|
|
879
|
+
for j in range(i + 1, len(lines)):
|
|
880
|
+
if lines[j].strip().startswith("["):
|
|
881
|
+
tool_section_end = j
|
|
882
|
+
break
|
|
883
|
+
if tool_section_end == -1:
|
|
884
|
+
tool_section_end = len(lines)
|
|
885
|
+
break
|
|
886
|
+
|
|
887
|
+
if tool_section_exists:
|
|
888
|
+
# Update existing section
|
|
889
|
+
patterns_str = ", ".join(f'"{p}"' for p in exclude_patterns)
|
|
890
|
+
has_exclude_patterns = False
|
|
891
|
+
for i in range(tool_section_index + 1, tool_section_end):
|
|
892
|
+
if "exclude-patterns" in lines[i]:
|
|
893
|
+
# Update existing line
|
|
894
|
+
lines[i] = f'exclude-patterns = [{patterns_str}]'
|
|
895
|
+
has_exclude_patterns = True
|
|
896
|
+
break
|
|
897
|
+
if not has_exclude_patterns:
|
|
898
|
+
# Add exclude-patterns to existing section
|
|
899
|
+
lines.insert(tool_section_end, f'exclude-patterns = [{patterns_str}]')
|
|
900
|
+
return "\n".join(lines)
|
|
901
|
+
else:
|
|
902
|
+
# Add new section at the end
|
|
903
|
+
patterns_str = ", ".join(f'"{p}"' for p in exclude_patterns)
|
|
904
|
+
lines.append("")
|
|
905
|
+
lines.append("[tool.python-package-folder]")
|
|
906
|
+
lines.append(f'exclude-patterns = [{patterns_str}]')
|
|
907
|
+
return "\n".join(lines)
|
|
908
|
+
|
|
640
909
|
def add_third_party_dependencies(self, dependencies: list[str]) -> None:
|
|
641
910
|
"""
|
|
642
911
|
Add third-party dependencies to the temporary pyproject.toml.
|
|
@@ -894,4 +1163,4 @@ class SubfolderBuildConfig:
|
|
|
894
1163
|
|
|
895
1164
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None: # noqa: ARG002
|
|
896
1165
|
"""Context manager exit - always restore."""
|
|
897
|
-
self.restore()
|
|
1166
|
+
self.restore()
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/utils.py
RENAMED
|
@@ -6,6 +6,14 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
+
try:
|
|
10
|
+
import tomllib
|
|
11
|
+
except ImportError:
|
|
12
|
+
try:
|
|
13
|
+
import tomli as tomllib
|
|
14
|
+
except ImportError:
|
|
15
|
+
tomllib = None
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|
def find_project_root(start_path: Path | None = None) -> Path | None:
|
|
11
19
|
"""
|
|
@@ -106,3 +114,64 @@ def is_python_package_directory(path: Path) -> bool:
|
|
|
106
114
|
return True
|
|
107
115
|
|
|
108
116
|
return False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def read_exclude_patterns(pyproject_path: Path) -> list[str]:
|
|
120
|
+
"""
|
|
121
|
+
Read exclude patterns from pyproject.toml.
|
|
122
|
+
|
|
123
|
+
Reads the exclude-patterns configuration from [tool.python-package-folder] section.
|
|
124
|
+
Returns an empty list if the section or option doesn't exist.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
pyproject_path: Path to the pyproject.toml file
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List of exclude patterns (regex strings), or empty list if not found
|
|
131
|
+
"""
|
|
132
|
+
if not pyproject_path.exists():
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
if tomllib:
|
|
137
|
+
content = pyproject_path.read_text(encoding="utf-8")
|
|
138
|
+
data = tomllib.loads(content)
|
|
139
|
+
tool_section = data.get("tool", {})
|
|
140
|
+
tool_config = tool_section.get("python-package-folder", {})
|
|
141
|
+
# TOML keys with hyphens are preserved as-is
|
|
142
|
+
patterns = tool_config.get("exclude-patterns", [])
|
|
143
|
+
if isinstance(patterns, list):
|
|
144
|
+
return [str(p) for p in patterns]
|
|
145
|
+
return []
|
|
146
|
+
else:
|
|
147
|
+
# Fallback: simple string parsing
|
|
148
|
+
content = pyproject_path.read_text(encoding="utf-8")
|
|
149
|
+
in_section = False
|
|
150
|
+
patterns = []
|
|
151
|
+
for line in content.split("\n"):
|
|
152
|
+
stripped = line.strip()
|
|
153
|
+
if stripped == "[tool.python-package-folder]":
|
|
154
|
+
in_section = True
|
|
155
|
+
continue
|
|
156
|
+
elif stripped.startswith("[") and in_section:
|
|
157
|
+
# Moved to a different section
|
|
158
|
+
break
|
|
159
|
+
elif in_section and stripped.startswith("exclude-patterns"):
|
|
160
|
+
# Parse the array
|
|
161
|
+
if "=" in stripped:
|
|
162
|
+
value = stripped.split("=", 1)[1].strip()
|
|
163
|
+
# Simple parsing for array of strings
|
|
164
|
+
if value.startswith("[") and value.endswith("]"):
|
|
165
|
+
# Remove brackets and split
|
|
166
|
+
items = value[1:-1].split(",")
|
|
167
|
+
for item in items:
|
|
168
|
+
item = item.strip().strip('"').strip("'")
|
|
169
|
+
if item:
|
|
170
|
+
patterns.append(item)
|
|
171
|
+
break
|
|
172
|
+
return patterns
|
|
173
|
+
except Exception:
|
|
174
|
+
# If parsing fails, return empty list
|
|
175
|
+
return []
|
|
176
|
+
|
|
177
|
+
return []
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Tests for exclude patterns functionality."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import zipfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from python_package_folder import BuildManager, SubfolderBuildConfig
|
|
11
|
+
from python_package_folder.utils import read_exclude_patterns
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def test_project_with_exclude_patterns(tmp_path: Path) -> Path:
|
|
16
|
+
"""Create a test project with exclude patterns in pyproject.toml."""
|
|
17
|
+
project_root = tmp_path / "test_project"
|
|
18
|
+
project_root.mkdir()
|
|
19
|
+
|
|
20
|
+
# Create pyproject.toml with exclude patterns
|
|
21
|
+
pyproject_content = """[project]
|
|
22
|
+
name = "test-package"
|
|
23
|
+
version = "0.1.0"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["hatchling"]
|
|
27
|
+
build-backend = "hatchling.build"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/test_package"]
|
|
31
|
+
|
|
32
|
+
[tool.python-package-folder]
|
|
33
|
+
exclude-patterns = ["_SS", ".*_test.*", "sandbox"]
|
|
34
|
+
"""
|
|
35
|
+
(project_root / "pyproject.toml").write_text(pyproject_content)
|
|
36
|
+
|
|
37
|
+
# Create source directory structure
|
|
38
|
+
src_dir = project_root / "src" / "test_package"
|
|
39
|
+
src_dir.mkdir(parents=True)
|
|
40
|
+
|
|
41
|
+
# Create regular files
|
|
42
|
+
(src_dir / "__init__.py").write_text("")
|
|
43
|
+
(src_dir / "module.py").write_text("def func(): pass")
|
|
44
|
+
|
|
45
|
+
# Create files that should be excluded
|
|
46
|
+
(src_dir / "module_test.py").write_text("# test file")
|
|
47
|
+
(src_dir / "test_data.py").write_text("# test data")
|
|
48
|
+
|
|
49
|
+
# Create directories that should be excluded
|
|
50
|
+
ss_dir = src_dir / "data_storage" / "_SS"
|
|
51
|
+
ss_dir.mkdir(parents=True)
|
|
52
|
+
(ss_dir / "file.py").write_text("# SS file")
|
|
53
|
+
|
|
54
|
+
sandbox_dir = src_dir / "sandbox"
|
|
55
|
+
sandbox_dir.mkdir()
|
|
56
|
+
(sandbox_dir / "file.py").write_text("# sandbox file")
|
|
57
|
+
|
|
58
|
+
# Create nested excluded directory
|
|
59
|
+
nested_ss = src_dir / "nested" / "_SS_nested"
|
|
60
|
+
nested_ss.mkdir(parents=True)
|
|
61
|
+
(nested_ss / "file.py").write_text("# nested SS file")
|
|
62
|
+
|
|
63
|
+
return project_root
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def test_subfolder_project(tmp_path: Path) -> Path:
|
|
68
|
+
"""Create a test project with subfolder and exclude patterns."""
|
|
69
|
+
project_root = tmp_path / "test_project"
|
|
70
|
+
project_root.mkdir()
|
|
71
|
+
|
|
72
|
+
# Create pyproject.toml with exclude patterns
|
|
73
|
+
pyproject_content = """[project]
|
|
74
|
+
name = "test-package"
|
|
75
|
+
version = "0.1.0"
|
|
76
|
+
|
|
77
|
+
[build-system]
|
|
78
|
+
requires = ["hatchling"]
|
|
79
|
+
build-backend = "hatchling.build"
|
|
80
|
+
|
|
81
|
+
[tool.hatch.build.targets.wheel]
|
|
82
|
+
packages = ["src/test_package"]
|
|
83
|
+
|
|
84
|
+
[tool.python-package-folder]
|
|
85
|
+
exclude-patterns = ["_SS", ".*_test.*"]
|
|
86
|
+
"""
|
|
87
|
+
(project_root / "pyproject.toml").write_text(pyproject_content)
|
|
88
|
+
|
|
89
|
+
# Create subfolder
|
|
90
|
+
subfolder = project_root / "subfolder"
|
|
91
|
+
subfolder.mkdir()
|
|
92
|
+
(subfolder / "__init__.py").write_text("")
|
|
93
|
+
(subfolder / "module.py").write_text("def func(): pass")
|
|
94
|
+
|
|
95
|
+
# Create files that should be excluded
|
|
96
|
+
(subfolder / "module_test.py").write_text("# test file")
|
|
97
|
+
ss_dir = subfolder / "_SS"
|
|
98
|
+
ss_dir.mkdir()
|
|
99
|
+
(ss_dir / "file.py").write_text("# SS file")
|
|
100
|
+
|
|
101
|
+
return project_root
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestReadExcludePatterns:
|
|
105
|
+
"""Tests for reading exclude patterns from pyproject.toml."""
|
|
106
|
+
|
|
107
|
+
def test_read_exclude_patterns_exists(self, test_project_with_exclude_patterns: Path) -> None:
|
|
108
|
+
"""Test reading exclude patterns when they exist."""
|
|
109
|
+
pyproject_path = test_project_with_exclude_patterns / "pyproject.toml"
|
|
110
|
+
patterns = read_exclude_patterns(pyproject_path)
|
|
111
|
+
|
|
112
|
+
assert len(patterns) == 3
|
|
113
|
+
assert "_SS" in patterns
|
|
114
|
+
assert ".*_test.*" in patterns
|
|
115
|
+
assert "sandbox" in patterns
|
|
116
|
+
|
|
117
|
+
def test_read_exclude_patterns_not_exists(self, tmp_path: Path) -> None:
|
|
118
|
+
"""Test reading exclude patterns when section doesn't exist."""
|
|
119
|
+
pyproject_path = tmp_path / "pyproject.toml"
|
|
120
|
+
pyproject_path.write_text("[project]\nname = 'test'")
|
|
121
|
+
|
|
122
|
+
patterns = read_exclude_patterns(pyproject_path)
|
|
123
|
+
assert patterns == []
|
|
124
|
+
|
|
125
|
+
def test_read_exclude_patterns_file_not_exists(self, tmp_path: Path) -> None:
|
|
126
|
+
"""Test reading exclude patterns when file doesn't exist."""
|
|
127
|
+
pyproject_path = tmp_path / "nonexistent.toml"
|
|
128
|
+
patterns = read_exclude_patterns(pyproject_path)
|
|
129
|
+
assert patterns == []
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class TestExcludePatternsInBuild:
|
|
133
|
+
"""Tests for exclude patterns in build process."""
|
|
134
|
+
|
|
135
|
+
def test_exclude_patterns_in_temp_pyproject(
|
|
136
|
+
self, test_project_with_exclude_patterns: Path
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Test that exclude patterns are injected into temporary pyproject.toml."""
|
|
139
|
+
project_root = test_project_with_exclude_patterns
|
|
140
|
+
src_dir = project_root / "src" / "test_package"
|
|
141
|
+
|
|
142
|
+
# This is detected as a subfolder build
|
|
143
|
+
config = SubfolderBuildConfig(
|
|
144
|
+
project_root=project_root,
|
|
145
|
+
src_dir=src_dir,
|
|
146
|
+
package_name="test-package",
|
|
147
|
+
version="1.0.0",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
temp_pyproject = config.create_temp_pyproject()
|
|
151
|
+
assert temp_pyproject is not None
|
|
152
|
+
|
|
153
|
+
# Check that exclude patterns are in the temporary pyproject.toml
|
|
154
|
+
content = temp_pyproject.read_text()
|
|
155
|
+
assert "[tool.python-package-folder]" in content
|
|
156
|
+
assert "exclude-patterns" in content
|
|
157
|
+
assert "_SS" in content
|
|
158
|
+
assert ".*_test.*" in content
|
|
159
|
+
assert "sandbox" in content
|
|
160
|
+
|
|
161
|
+
config.restore()
|
|
162
|
+
|
|
163
|
+
def test_exclude_patterns_subfolder_build(self, test_subfolder_project: Path) -> None:
|
|
164
|
+
"""Test that exclude patterns from root are applied to subfolder builds."""
|
|
165
|
+
project_root = test_subfolder_project
|
|
166
|
+
subfolder = project_root / "subfolder"
|
|
167
|
+
|
|
168
|
+
config = SubfolderBuildConfig(
|
|
169
|
+
project_root=project_root,
|
|
170
|
+
src_dir=subfolder,
|
|
171
|
+
package_name="subfolder",
|
|
172
|
+
version="1.0.0",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Create temporary pyproject.toml
|
|
176
|
+
temp_pyproject = config.create_temp_pyproject()
|
|
177
|
+
assert temp_pyproject is not None
|
|
178
|
+
|
|
179
|
+
# Check that exclude patterns are in the temporary pyproject.toml
|
|
180
|
+
content = temp_pyproject.read_text()
|
|
181
|
+
assert "[tool.python-package-folder]" in content
|
|
182
|
+
assert "exclude-patterns" in content
|
|
183
|
+
assert "_SS" in content
|
|
184
|
+
assert ".*_test.*" in content
|
|
185
|
+
|
|
186
|
+
config.restore()
|
|
@@ -661,7 +661,7 @@ class TestSubfolderBuildTemporaryPyprojectCreation:
|
|
|
661
661
|
|
|
662
662
|
# Verify build-system section is added (required for hatchling)
|
|
663
663
|
assert "[build-system]" in content
|
|
664
|
-
assert 'requires = ["hatchling"]' in content
|
|
664
|
+
assert 'requires = ["hatchling", "python-package-folder"]' in content
|
|
665
665
|
assert 'build-backend = "hatchling.build"' in content
|
|
666
666
|
assert "[tool.hatch.version]" not in content
|
|
667
667
|
assert "[tool.uv-dynamic-versioning]" not in content
|
|
@@ -894,7 +894,7 @@ description = "Subfolder package"
|
|
|
894
894
|
|
|
895
895
|
# Verify build-system section uses hatchling, not setuptools
|
|
896
896
|
assert "[build-system]" in content
|
|
897
|
-
assert 'requires = ["hatchling"]' in content
|
|
897
|
+
assert 'requires = ["hatchling", "python-package-folder"]' in content
|
|
898
898
|
assert 'build-backend = "hatchling.build"' in content
|
|
899
899
|
assert "setuptools" not in content or 'build-backend = "setuptools' not in content
|
|
900
900
|
|
|
@@ -929,7 +929,7 @@ description = "Subfolder package"
|
|
|
929
929
|
|
|
930
930
|
# Verify build-system section is added
|
|
931
931
|
assert "[build-system]" in content
|
|
932
|
-
assert 'requires = ["hatchling"]' in content
|
|
932
|
+
assert 'requires = ["hatchling", "python-package-folder"]' in content
|
|
933
933
|
assert 'build-backend = "hatchling.build"' in content
|
|
934
934
|
|
|
935
935
|
config.restore()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/finder.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/manager.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/publisher.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/folder_structure/some_globals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_build_with_external_deps.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-5.0.1 → python_package_folder-5.1.1}/tests/test_version_calculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|