python-package-folder 9.1.0__tar.gz → 9.2.0__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-9.1.0 → python_package_folder-9.2.0}/PKG-INFO +1 -1
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/pyproject.toml +1 -1
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/analyzer.py +16 -0
- python_package_folder-9.2.0/tests/test_subfolder_build.py +62 -0
- python_package_folder-9.2.0/tests/test_subfolder_build_config.py +399 -0
- python_package_folder-9.2.0/tests/test_subfolder_imports.py +600 -0
- python_package_folder-9.2.0/tests/test_subfolder_pyproject_toml.py +1145 -0
- python_package_folder-9.2.0/tests/test_subfolder_temp_directory.py +413 -0
- python_package_folder-9.2.0/tests/test_subfolder_wheel_packaging.py +124 -0
- python_package_folder-9.1.0/tests/test_subfolder_build.py +0 -4474
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.copier-answers.yml +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.github/workflows/ci.yml +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.github/workflows/publish.yml +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.gitignore +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/.vscode/settings.json +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/LICENSE +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/MANIFEST.in +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/Makefile +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/README.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/coverage.svg +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/development.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/DEVELOPMENT.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/INSTALLATION.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/PUBLISHING.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/REFERENCE.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/USAGE.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/docs/VERSION_RESOLUTION.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/installation.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/publishing.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/types.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/version.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/version_calculator.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/conftest.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_exclude_patterns.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_linting.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_publisher.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_utils.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_version_calculator.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/test_version_manager.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/tests/tests.py +0 -0
- {python_package_folder-9.1.0 → python_package_folder-9.2.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.2.0
|
|
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>
|
{python_package_folder-9.1.0 → python_package_folder-9.2.0}/src/python_package_folder/analyzer.py
RENAMED
|
@@ -310,6 +310,15 @@ class ImportAnalyzer:
|
|
|
310
310
|
self.project_root / module_path_str / "__init__.py",
|
|
311
311
|
(self.project_root / module_path_str).with_suffix(".py"),
|
|
312
312
|
]
|
|
313
|
+
|
|
314
|
+
# Also check project_root/src/ for files at src/ root (like _globals.py)
|
|
315
|
+
# This is important when building subfolders where src_dir points to temp directory
|
|
316
|
+
src_base = self.project_root / "src"
|
|
317
|
+
if src_base.exists():
|
|
318
|
+
potential_paths.extend([
|
|
319
|
+
src_base / module_path_str / "__init__.py",
|
|
320
|
+
(src_base / module_path_str).with_suffix(".py"),
|
|
321
|
+
])
|
|
313
322
|
|
|
314
323
|
for path in potential_paths:
|
|
315
324
|
if path.exists():
|
|
@@ -335,6 +344,13 @@ class ImportAnalyzer:
|
|
|
335
344
|
potential_file = parent / f"{module_name.split('.')[-1]}.py"
|
|
336
345
|
if potential_file.exists():
|
|
337
346
|
return potential_file
|
|
347
|
+
|
|
348
|
+
# Also check project_root/src/ for files at src/ root
|
|
349
|
+
# This handles cases like _globals.py at src/_globals.py when building subfolders
|
|
350
|
+
if parent == self.project_root and src_base.exists():
|
|
351
|
+
src_file = src_base / f"{module_name.split('.')[-1]}.py"
|
|
352
|
+
if src_file.exists():
|
|
353
|
+
return src_file
|
|
338
354
|
|
|
339
355
|
# Check all subdirectories in parent (not just common ones)
|
|
340
356
|
# This handles cases like src/data/spreadsheet_creation/spreadsheet_formatting_dataclasses.py
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Tests for subfolder build functionality."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import venv
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from python_package_folder import BuildManager, SubfolderBuildConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def test_project_with_pyproject(tmp_path: Path) -> Path:
|
|
19
|
+
"""Create a test project with pyproject.toml."""
|
|
20
|
+
project_root = tmp_path / "test_project"
|
|
21
|
+
project_root.mkdir()
|
|
22
|
+
|
|
23
|
+
# Create pyproject.toml
|
|
24
|
+
pyproject_content = """[project]
|
|
25
|
+
name = "test-package"
|
|
26
|
+
version = "0.1.0"
|
|
27
|
+
dynamic = ["version"]
|
|
28
|
+
|
|
29
|
+
[tool.hatch.version]
|
|
30
|
+
source = "uv-dynamic-versioning"
|
|
31
|
+
|
|
32
|
+
[tool.uv-dynamic-versioning]
|
|
33
|
+
vcs = "git"
|
|
34
|
+
style = "pep440"
|
|
35
|
+
bump = true
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/test_package"]
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8.0.0",
|
|
43
|
+
"pytest-cov>=4.0.0",
|
|
44
|
+
]
|
|
45
|
+
test = [
|
|
46
|
+
"pytest>=8.0.0",
|
|
47
|
+
"mypy>=1.0.0",
|
|
48
|
+
]
|
|
49
|
+
"""
|
|
50
|
+
(project_root / "pyproject.toml").write_text(pyproject_content)
|
|
51
|
+
|
|
52
|
+
# Create subfolder
|
|
53
|
+
subfolder = project_root / "subfolder"
|
|
54
|
+
subfolder.mkdir(exist_ok=True)
|
|
55
|
+
(subfolder / "module.py").write_text("def func(): pass")
|
|
56
|
+
|
|
57
|
+
return project_root
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# This file has been split into multiple test files.
|
|
62
|
+
# See test_subfolder_*.py files for the actual tests.
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""Tests for subfolder build functionality."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import venv
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from python_package_folder import BuildManager, SubfolderBuildConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def test_project_with_pyproject(tmp_path: Path) -> Path:
|
|
19
|
+
"""Create a test project with pyproject.toml."""
|
|
20
|
+
project_root = tmp_path / "test_project"
|
|
21
|
+
project_root.mkdir()
|
|
22
|
+
|
|
23
|
+
# Create pyproject.toml
|
|
24
|
+
pyproject_content = """[project]
|
|
25
|
+
name = "test-package"
|
|
26
|
+
version = "0.1.0"
|
|
27
|
+
dynamic = ["version"]
|
|
28
|
+
|
|
29
|
+
[tool.hatch.version]
|
|
30
|
+
source = "uv-dynamic-versioning"
|
|
31
|
+
|
|
32
|
+
[tool.uv-dynamic-versioning]
|
|
33
|
+
vcs = "git"
|
|
34
|
+
style = "pep440"
|
|
35
|
+
bump = true
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/test_package"]
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8.0.0",
|
|
43
|
+
"pytest-cov>=4.0.0",
|
|
44
|
+
]
|
|
45
|
+
test = [
|
|
46
|
+
"pytest>=8.0.0",
|
|
47
|
+
"mypy>=1.0.0",
|
|
48
|
+
]
|
|
49
|
+
"""
|
|
50
|
+
(project_root / "pyproject.toml").write_text(pyproject_content)
|
|
51
|
+
|
|
52
|
+
# Create subfolder
|
|
53
|
+
subfolder = project_root / "subfolder"
|
|
54
|
+
subfolder.mkdir(exist_ok=True)
|
|
55
|
+
(subfolder / "module.py").write_text("def func(): pass")
|
|
56
|
+
|
|
57
|
+
return project_root
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestSubfolderBuildConfig:
|
|
61
|
+
"""
|
|
62
|
+
Tests for SubfolderBuildConfig class.
|
|
63
|
+
|
|
64
|
+
This class tests the core functionality of SubfolderBuildConfig, including:
|
|
65
|
+
- Initialization with default and custom package names
|
|
66
|
+
- Temporary pyproject.toml creation and configuration
|
|
67
|
+
- Dependency group handling
|
|
68
|
+
- __init__.py file creation
|
|
69
|
+
|
|
70
|
+
File: test_subfolder_build_config.py
|
|
71
|
+
When to add tests here: Tests for SubfolderBuildConfig initialization, configuration,
|
|
72
|
+
and temporary file management should go in this class.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def test_init_with_defaults(self, test_project_with_pyproject: Path) -> None:
|
|
76
|
+
"""Test initialization with default package name."""
|
|
77
|
+
config = SubfolderBuildConfig(
|
|
78
|
+
project_root=test_project_with_pyproject,
|
|
79
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
80
|
+
version="1.0.0",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
assert config.package_name == "test-package-subfolder"
|
|
84
|
+
assert config.version == "1.0.0"
|
|
85
|
+
assert config.dependency_group is None
|
|
86
|
+
|
|
87
|
+
def test_init_with_custom_name(self, test_project_with_pyproject: Path) -> None:
|
|
88
|
+
"""Test initialization with custom package name."""
|
|
89
|
+
config = SubfolderBuildConfig(
|
|
90
|
+
project_root=test_project_with_pyproject,
|
|
91
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
92
|
+
package_name="custom-package",
|
|
93
|
+
version="1.0.0",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
assert config.package_name == "custom-package"
|
|
97
|
+
assert config.version == "1.0.0"
|
|
98
|
+
|
|
99
|
+
def test_init_with_dependency_group(self, test_project_with_pyproject: Path) -> None:
|
|
100
|
+
"""Test initialization with dependency group."""
|
|
101
|
+
config = SubfolderBuildConfig(
|
|
102
|
+
project_root=test_project_with_pyproject,
|
|
103
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
104
|
+
version="1.0.0",
|
|
105
|
+
dependency_group="dev",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert config.dependency_group == "dev"
|
|
109
|
+
|
|
110
|
+
def test_create_temp_pyproject(self, test_project_with_pyproject: Path) -> None:
|
|
111
|
+
"""Test creating temporary pyproject.toml."""
|
|
112
|
+
config = SubfolderBuildConfig(
|
|
113
|
+
project_root=test_project_with_pyproject,
|
|
114
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
115
|
+
version="2.0.0",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
pyproject_path = config.create_temp_pyproject()
|
|
119
|
+
|
|
120
|
+
assert pyproject_path.exists()
|
|
121
|
+
content = pyproject_path.read_text()
|
|
122
|
+
|
|
123
|
+
# Check package name and version are set
|
|
124
|
+
assert 'name = "test-package-subfolder"' in content
|
|
125
|
+
assert 'version = "2.0.0"' in content
|
|
126
|
+
|
|
127
|
+
# Check dynamic versioning is removed
|
|
128
|
+
assert 'dynamic = ["version"]' not in content
|
|
129
|
+
assert "[tool.hatch.version]" not in content
|
|
130
|
+
assert "[tool.uv-dynamic-versioning]" not in content
|
|
131
|
+
|
|
132
|
+
def test_create_temp_pyproject_with_dependency_group(
|
|
133
|
+
self, test_project_with_pyproject: Path
|
|
134
|
+
) -> None:
|
|
135
|
+
"""Test creating temporary pyproject.toml with dependency group."""
|
|
136
|
+
config = SubfolderBuildConfig(
|
|
137
|
+
project_root=test_project_with_pyproject,
|
|
138
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
139
|
+
version="2.0.0",
|
|
140
|
+
dependency_group="dev",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
pyproject_path = config.create_temp_pyproject()
|
|
144
|
+
content = pyproject_path.read_text()
|
|
145
|
+
|
|
146
|
+
# Check dependency group is included
|
|
147
|
+
assert "[dependency-groups]" in content
|
|
148
|
+
assert "dev = [" in content
|
|
149
|
+
assert '"pytest>=8.0.0"' in content
|
|
150
|
+
|
|
151
|
+
def test_create_temp_pyproject_creates_init(self, test_project_with_pyproject: Path) -> None:
|
|
152
|
+
"""Test that __init__.py is created if missing."""
|
|
153
|
+
subfolder = test_project_with_pyproject / "subfolder"
|
|
154
|
+
config = SubfolderBuildConfig(
|
|
155
|
+
project_root=test_project_with_pyproject,
|
|
156
|
+
src_dir=subfolder,
|
|
157
|
+
version="1.0.0",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Ensure __init__.py doesn't exist
|
|
161
|
+
init_file = subfolder / "__init__.py"
|
|
162
|
+
if init_file.exists():
|
|
163
|
+
init_file.unlink()
|
|
164
|
+
|
|
165
|
+
config.create_temp_pyproject()
|
|
166
|
+
|
|
167
|
+
# Check __init__.py was created
|
|
168
|
+
assert init_file.exists()
|
|
169
|
+
assert config._temp_init_created
|
|
170
|
+
|
|
171
|
+
def test_restore_pyproject(self, test_project_with_pyproject: Path) -> None:
|
|
172
|
+
"""Test restoring original pyproject.toml."""
|
|
173
|
+
original_content = (test_project_with_pyproject / "pyproject.toml").read_text()
|
|
174
|
+
|
|
175
|
+
config = SubfolderBuildConfig(
|
|
176
|
+
project_root=test_project_with_pyproject,
|
|
177
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
178
|
+
version="1.0.0",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
config.create_temp_pyproject()
|
|
182
|
+
config.restore()
|
|
183
|
+
|
|
184
|
+
# Check original content is restored
|
|
185
|
+
restored_content = (test_project_with_pyproject / "pyproject.toml").read_text()
|
|
186
|
+
assert restored_content == original_content
|
|
187
|
+
|
|
188
|
+
# Check backup is removed
|
|
189
|
+
assert not (test_project_with_pyproject / "pyproject.toml.original").exists()
|
|
190
|
+
|
|
191
|
+
def test_restore_removes_temp_init(self, test_project_with_pyproject: Path) -> None:
|
|
192
|
+
"""Test that restore removes temporary __init__.py."""
|
|
193
|
+
subfolder = test_project_with_pyproject / "subfolder"
|
|
194
|
+
init_file = subfolder / "__init__.py"
|
|
195
|
+
|
|
196
|
+
# Ensure __init__.py doesn't exist
|
|
197
|
+
if init_file.exists():
|
|
198
|
+
init_file.unlink()
|
|
199
|
+
|
|
200
|
+
config = SubfolderBuildConfig(
|
|
201
|
+
project_root=test_project_with_pyproject,
|
|
202
|
+
src_dir=subfolder,
|
|
203
|
+
version="1.0.0",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
config.create_temp_pyproject()
|
|
207
|
+
assert init_file.exists()
|
|
208
|
+
|
|
209
|
+
config.restore()
|
|
210
|
+
|
|
211
|
+
# Check __init__.py was removed
|
|
212
|
+
assert not init_file.exists()
|
|
213
|
+
|
|
214
|
+
def test_restore_preserves_existing_init(self, test_project_with_pyproject: Path) -> None:
|
|
215
|
+
"""Test that restore preserves existing __init__.py."""
|
|
216
|
+
subfolder = test_project_with_pyproject / "subfolder"
|
|
217
|
+
init_file = subfolder / "__init__.py"
|
|
218
|
+
|
|
219
|
+
# Create existing __init__.py
|
|
220
|
+
init_file.write_text("# Original content")
|
|
221
|
+
|
|
222
|
+
config = SubfolderBuildConfig(
|
|
223
|
+
project_root=test_project_with_pyproject,
|
|
224
|
+
src_dir=subfolder,
|
|
225
|
+
version="1.0.0",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
config.create_temp_pyproject()
|
|
229
|
+
config.restore()
|
|
230
|
+
|
|
231
|
+
# Check original __init__.py is preserved
|
|
232
|
+
assert init_file.exists()
|
|
233
|
+
assert init_file.read_text() == "# Original content"
|
|
234
|
+
|
|
235
|
+
def test_context_manager(self, test_project_with_pyproject: Path) -> None:
|
|
236
|
+
"""Test using SubfolderBuildConfig as context manager."""
|
|
237
|
+
original_content = (test_project_with_pyproject / "pyproject.toml").read_text()
|
|
238
|
+
|
|
239
|
+
with SubfolderBuildConfig(
|
|
240
|
+
project_root=test_project_with_pyproject,
|
|
241
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
242
|
+
version="1.0.0",
|
|
243
|
+
) as config:
|
|
244
|
+
config.create_temp_pyproject()
|
|
245
|
+
content = (test_project_with_pyproject / "pyproject.toml").read_text()
|
|
246
|
+
assert 'name = "test-package-subfolder"' in content
|
|
247
|
+
|
|
248
|
+
# Check restore happened automatically
|
|
249
|
+
restored_content = (test_project_with_pyproject / "pyproject.toml").read_text()
|
|
250
|
+
assert restored_content == original_content
|
|
251
|
+
|
|
252
|
+
def test_missing_dependency_group_warning(
|
|
253
|
+
self, test_project_with_pyproject: Path, capsys: pytest.CaptureFixture[str]
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Test warning when dependency group doesn't exist."""
|
|
256
|
+
config = SubfolderBuildConfig(
|
|
257
|
+
project_root=test_project_with_pyproject,
|
|
258
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
259
|
+
version="1.0.0",
|
|
260
|
+
dependency_group="nonexistent",
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Create temp pyproject - this should print a warning
|
|
264
|
+
config.create_temp_pyproject()
|
|
265
|
+
|
|
266
|
+
# The warning is printed to stderr during create_temp_pyproject
|
|
267
|
+
# Since capsys might not capture it properly, we'll just verify
|
|
268
|
+
# that the build still works (warning is non-fatal)
|
|
269
|
+
assert config.temp_pyproject is not None
|
|
270
|
+
|
|
271
|
+
def test_version_required(self, test_project_with_pyproject: Path) -> None:
|
|
272
|
+
"""Test that version is required."""
|
|
273
|
+
config = SubfolderBuildConfig(
|
|
274
|
+
project_root=test_project_with_pyproject,
|
|
275
|
+
src_dir=test_project_with_pyproject / "subfolder",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
with pytest.raises(ValueError, match="Version is required"):
|
|
279
|
+
config.create_temp_pyproject()
|
|
280
|
+
|
|
281
|
+
def test_package_name_derivation(self, test_project_with_pyproject: Path) -> None:
|
|
282
|
+
"""Test package name derivation from root project name and directory name."""
|
|
283
|
+
# Test with underscores
|
|
284
|
+
subfolder = test_project_with_pyproject / "subfolder_to_build"
|
|
285
|
+
subfolder.mkdir(exist_ok=True)
|
|
286
|
+
config = SubfolderBuildConfig(
|
|
287
|
+
project_root=test_project_with_pyproject,
|
|
288
|
+
src_dir=subfolder,
|
|
289
|
+
version="1.0.0",
|
|
290
|
+
)
|
|
291
|
+
assert config.package_name == "test-package-subfolder-to-build"
|
|
292
|
+
|
|
293
|
+
# Test with spaces
|
|
294
|
+
subfolder2 = test_project_with_pyproject / "subfolder with spaces"
|
|
295
|
+
subfolder2.mkdir()
|
|
296
|
+
config2 = SubfolderBuildConfig(
|
|
297
|
+
project_root=test_project_with_pyproject,
|
|
298
|
+
src_dir=subfolder2,
|
|
299
|
+
version="1.0.0",
|
|
300
|
+
)
|
|
301
|
+
assert config2.package_name == "test-package-subfolder-with-spaces"
|
|
302
|
+
|
|
303
|
+
def test_package_name_derivation_no_root_project(self, tmp_path: Path) -> None:
|
|
304
|
+
"""Test package name derivation when root project name is not found (fallback)."""
|
|
305
|
+
# Create a project without pyproject.toml
|
|
306
|
+
project_root = tmp_path / "test_project_no_pyproject"
|
|
307
|
+
project_root.mkdir()
|
|
308
|
+
|
|
309
|
+
subfolder = project_root / "subfolder"
|
|
310
|
+
subfolder.mkdir(exist_ok=True)
|
|
311
|
+
|
|
312
|
+
config = SubfolderBuildConfig(
|
|
313
|
+
project_root=project_root,
|
|
314
|
+
src_dir=subfolder,
|
|
315
|
+
version="1.0.0",
|
|
316
|
+
)
|
|
317
|
+
# Should fallback to just subfolder name when root project name not found
|
|
318
|
+
assert config.package_name == "subfolder"
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def test_readme_handling_with_existing_readme(test_project_with_pyproject: Path):
|
|
322
|
+
"""Test that subfolder README is used when it exists."""
|
|
323
|
+
project_root = test_project_with_pyproject
|
|
324
|
+
subfolder = project_root / "subfolder"
|
|
325
|
+
|
|
326
|
+
# Create README in subfolder
|
|
327
|
+
subfolder_readme = subfolder / "README.md"
|
|
328
|
+
subfolder_readme.write_text("# Subfolder Package\n\nThis is the subfolder README.")
|
|
329
|
+
|
|
330
|
+
# Create README in project root
|
|
331
|
+
project_readme = project_root / "README.md"
|
|
332
|
+
project_readme.write_text("# Parent Package\n\nThis is the parent README.")
|
|
333
|
+
|
|
334
|
+
config = SubfolderBuildConfig(
|
|
335
|
+
project_root=project_root,
|
|
336
|
+
src_dir=subfolder,
|
|
337
|
+
version="1.0.0",
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
config.create_temp_pyproject()
|
|
342
|
+
|
|
343
|
+
# Check that subfolder README was copied to project root
|
|
344
|
+
assert (project_root / "README.md").exists()
|
|
345
|
+
content = (project_root / "README.md").read_text()
|
|
346
|
+
assert "Subfolder Package" in content
|
|
347
|
+
assert "This is the subfolder README" in content
|
|
348
|
+
assert "Parent Package" not in content
|
|
349
|
+
|
|
350
|
+
# Check that backup was created
|
|
351
|
+
assert (project_root / "README.md.backup").exists()
|
|
352
|
+
backup_content = (project_root / "README.md.backup").read_text()
|
|
353
|
+
assert "Parent Package" in backup_content
|
|
354
|
+
finally:
|
|
355
|
+
config.restore()
|
|
356
|
+
|
|
357
|
+
# Verify original README was restored
|
|
358
|
+
assert (project_root / "README.md").exists()
|
|
359
|
+
restored_content = (project_root / "README.md").read_text()
|
|
360
|
+
assert "Parent Package" in restored_content
|
|
361
|
+
assert "Subfolder Package" not in restored_content
|
|
362
|
+
assert not (project_root / "README.md.backup").exists()
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_readme_handling_without_readme(test_project_with_pyproject: Path):
|
|
366
|
+
"""Test that minimal README is created when subfolder has no README."""
|
|
367
|
+
project_root = test_project_with_pyproject
|
|
368
|
+
subfolder = project_root / "subfolder"
|
|
369
|
+
|
|
370
|
+
# Ensure no README exists
|
|
371
|
+
assert not (subfolder / "README.md").exists()
|
|
372
|
+
assert not (subfolder / "README.rst").exists()
|
|
373
|
+
|
|
374
|
+
config = SubfolderBuildConfig(
|
|
375
|
+
project_root=project_root,
|
|
376
|
+
src_dir=subfolder,
|
|
377
|
+
version="1.0.0",
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
try:
|
|
381
|
+
config.create_temp_pyproject()
|
|
382
|
+
|
|
383
|
+
# Check that minimal README was created
|
|
384
|
+
assert (project_root / "README.md").exists()
|
|
385
|
+
content = (project_root / "README.md").read_text()
|
|
386
|
+
assert content.strip() == f"# {subfolder.name}"
|
|
387
|
+
finally:
|
|
388
|
+
config.restore()
|
|
389
|
+
|
|
390
|
+
# Verify README was removed if it didn't exist before
|
|
391
|
+
if not (project_root / "README.md.backup").exists():
|
|
392
|
+
# No backup means no original README, so temp should be removed
|
|
393
|
+
assert (
|
|
394
|
+
not (project_root / "README.md").exists()
|
|
395
|
+
or (project_root / "README.md").read_text() != f"# {subfolder.name}\n"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|