python-package-folder 3.1.0__tar.gz → 3.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.
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/PKG-INFO +1 -1
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/coverage.svg +2 -2
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/manager.py +160 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/subfolder_build.py +44 -74
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_build_with_external_deps.py +88 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_subfolder_build.py +27 -34
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.copier-answers.yml +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.github/workflows/ci.yml +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.github/workflows/publish.yml +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.gitignore +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/.vscode/settings.json +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/LICENSE +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/Makefile +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/README.md +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/development.md +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/installation.md +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/publishing.md +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/pyproject.toml +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/types.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/version.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/conftest.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_linting.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_publisher.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_utils.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_version_manager.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/tests.py +0 -0
- {python_package_folder-3.1.0 → python_package_folder-3.1.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.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">
|
|
18
|
-
<text x="81" y="14">
|
|
17
|
+
<text x="81" y="15" fill="#010101" fill-opacity=".3">68%</text>
|
|
18
|
+
<text x="81" y="14">68%</text>
|
|
19
19
|
</g>
|
|
20
20
|
</svg>
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/manager.py
RENAMED
|
@@ -88,6 +88,8 @@ class BuildManager:
|
|
|
88
88
|
self.subfolder_config: SubfolderBuildConfig | None = None
|
|
89
89
|
# Cache for package name lookups (expensive operation)
|
|
90
90
|
self._packages_distributions_cache: dict[str, list[str]] | None = None
|
|
91
|
+
# Track files with modified imports and their original content
|
|
92
|
+
self._modified_import_files: dict[Path, str] = {}
|
|
91
93
|
|
|
92
94
|
# Check if it's a valid Python package directory
|
|
93
95
|
if not any(self.src_dir.glob("*.py")) and not (self.src_dir / "__init__.py").exists():
|
|
@@ -270,6 +272,10 @@ class BuildManager:
|
|
|
270
272
|
for dep in external_deps:
|
|
271
273
|
self._copy_dependency(dep)
|
|
272
274
|
|
|
275
|
+
# For subfolder builds, convert absolute imports of copied dependencies to relative imports
|
|
276
|
+
if self._is_subfolder_build() and external_deps:
|
|
277
|
+
self._convert_copied_dependency_imports_to_relative(python_files, external_deps)
|
|
278
|
+
|
|
273
279
|
# For subfolder builds, extract third-party dependencies and add to pyproject.toml
|
|
274
280
|
if self._is_subfolder_build() and self.subfolder_config:
|
|
275
281
|
# Re-analyze all Python files (including copied dependencies) to find third-party imports
|
|
@@ -629,6 +635,147 @@ class BuildManager:
|
|
|
629
635
|
|
|
630
636
|
return sorted(list(third_party_packages))
|
|
631
637
|
|
|
638
|
+
def _convert_copied_dependency_imports_to_relative(
|
|
639
|
+
self, python_files: list[Path], external_deps: list[ExternalDependency]
|
|
640
|
+
) -> None:
|
|
641
|
+
"""
|
|
642
|
+
Convert absolute imports of copied dependencies to relative imports.
|
|
643
|
+
|
|
644
|
+
For subfolder builds, when external dependencies are copied into the subfolder,
|
|
645
|
+
imports in the subfolder's files need to be converted from absolute to relative
|
|
646
|
+
so they work correctly when the package is installed.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
python_files: List of Python files in the source directory
|
|
650
|
+
external_deps: List of external dependencies that were copied
|
|
651
|
+
"""
|
|
652
|
+
import ast
|
|
653
|
+
import re
|
|
654
|
+
|
|
655
|
+
# Build a set of import names that were copied (e.g., "_shared", "_globals", "empty_drawing_detection_config")
|
|
656
|
+
copied_import_names: set[str] = set()
|
|
657
|
+
for dep in external_deps:
|
|
658
|
+
# Get the root module name (first part of the import)
|
|
659
|
+
root_module = dep.import_name.split(".")[0]
|
|
660
|
+
copied_import_names.add(root_module)
|
|
661
|
+
# Also add the full module name for nested imports
|
|
662
|
+
copied_import_names.add(dep.import_name)
|
|
663
|
+
|
|
664
|
+
if not copied_import_names:
|
|
665
|
+
return
|
|
666
|
+
|
|
667
|
+
# Only modify files that are in the original subfolder (not the copied dependencies)
|
|
668
|
+
for file_path in python_files:
|
|
669
|
+
# Skip files that are part of copied dependencies
|
|
670
|
+
is_copied_file = any(file_path.is_relative_to(dep.target_path) for dep in external_deps)
|
|
671
|
+
if is_copied_file:
|
|
672
|
+
continue
|
|
673
|
+
|
|
674
|
+
# Skip if file is not in src_dir (shouldn't happen, but safety check)
|
|
675
|
+
if not file_path.is_relative_to(self.src_dir):
|
|
676
|
+
continue
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
content = file_path.read_text(encoding="utf-8")
|
|
680
|
+
original_content = content
|
|
681
|
+
lines = content.split("\n")
|
|
682
|
+
modified = False
|
|
683
|
+
|
|
684
|
+
# Parse the file with AST to find imports accurately
|
|
685
|
+
try:
|
|
686
|
+
tree = ast.parse(content, filename=str(file_path))
|
|
687
|
+
except SyntaxError:
|
|
688
|
+
# Skip files with syntax errors
|
|
689
|
+
continue
|
|
690
|
+
|
|
691
|
+
# Track which lines need to be modified
|
|
692
|
+
lines_to_modify: dict[int, str] = {}
|
|
693
|
+
|
|
694
|
+
for node in ast.walk(tree):
|
|
695
|
+
if isinstance(node, ast.ImportFrom):
|
|
696
|
+
if node.module is None:
|
|
697
|
+
continue
|
|
698
|
+
|
|
699
|
+
# Check if this import matches a copied dependency
|
|
700
|
+
root_module = node.module.split(".")[0]
|
|
701
|
+
if (
|
|
702
|
+
root_module not in copied_import_names
|
|
703
|
+
and node.module not in copied_import_names
|
|
704
|
+
):
|
|
705
|
+
continue
|
|
706
|
+
|
|
707
|
+
# Get the line content
|
|
708
|
+
line_num = node.lineno - 1 # Convert to 0-based index
|
|
709
|
+
if line_num < 0 or line_num >= len(lines):
|
|
710
|
+
continue
|
|
711
|
+
|
|
712
|
+
original_line = lines[line_num]
|
|
713
|
+
|
|
714
|
+
# Skip if already a relative import
|
|
715
|
+
if original_line.strip().startswith("from ."):
|
|
716
|
+
continue
|
|
717
|
+
|
|
718
|
+
# Convert absolute import to relative import
|
|
719
|
+
# from _shared.image_utils import ... -> from ._shared.image_utils import ...
|
|
720
|
+
new_line = re.sub(
|
|
721
|
+
rf"^(\s*)from\s+{re.escape(node.module)}\s+import",
|
|
722
|
+
rf"\1from .{node.module} import",
|
|
723
|
+
original_line,
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
if new_line != original_line:
|
|
727
|
+
lines_to_modify[line_num] = new_line
|
|
728
|
+
modified = True
|
|
729
|
+
|
|
730
|
+
elif isinstance(node, ast.Import):
|
|
731
|
+
# Handle "import X" statements
|
|
732
|
+
for alias in node.names:
|
|
733
|
+
root_module = alias.name.split(".")[0]
|
|
734
|
+
if root_module not in copied_import_names:
|
|
735
|
+
continue
|
|
736
|
+
|
|
737
|
+
line_num = node.lineno - 1
|
|
738
|
+
if line_num < 0 or line_num >= len(lines):
|
|
739
|
+
continue
|
|
740
|
+
|
|
741
|
+
original_line = lines[line_num]
|
|
742
|
+
|
|
743
|
+
# Skip if already a relative import
|
|
744
|
+
if original_line.strip().startswith("import ."):
|
|
745
|
+
continue
|
|
746
|
+
|
|
747
|
+
# Convert "import _shared" to "from . import _shared"
|
|
748
|
+
# This is more complex, so we'll use a regex replacement
|
|
749
|
+
new_line = re.sub(
|
|
750
|
+
rf"^(\s*)import\s+{re.escape(alias.name)}\b",
|
|
751
|
+
rf"\1from . import {alias.name}",
|
|
752
|
+
original_line,
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
if new_line != original_line:
|
|
756
|
+
lines_to_modify[line_num] = new_line
|
|
757
|
+
modified = True
|
|
758
|
+
|
|
759
|
+
# Apply modifications
|
|
760
|
+
if modified:
|
|
761
|
+
for line_num, new_line in lines_to_modify.items():
|
|
762
|
+
lines[line_num] = new_line
|
|
763
|
+
|
|
764
|
+
new_content = "\n".join(lines)
|
|
765
|
+
# Store original content for restoration
|
|
766
|
+
if file_path not in self._modified_import_files:
|
|
767
|
+
self._modified_import_files[file_path] = original_content
|
|
768
|
+
|
|
769
|
+
# Write modified content
|
|
770
|
+
file_path.write_text(new_content, encoding="utf-8")
|
|
771
|
+
print(f"Converted imports to relative in: {file_path}")
|
|
772
|
+
|
|
773
|
+
except Exception as e:
|
|
774
|
+
print(
|
|
775
|
+
f"Warning: Could not modify imports in {file_path}: {e}",
|
|
776
|
+
file=sys.stderr,
|
|
777
|
+
)
|
|
778
|
+
|
|
632
779
|
def _report_ambiguous_imports(self, python_files: list[Path]) -> None:
|
|
633
780
|
"""
|
|
634
781
|
Report any ambiguous imports that couldn't be resolved.
|
|
@@ -708,6 +855,19 @@ class BuildManager:
|
|
|
708
855
|
self.copied_files.clear()
|
|
709
856
|
self.copied_dirs.clear()
|
|
710
857
|
|
|
858
|
+
# Restore files with modified imports
|
|
859
|
+
for file_path, original_content in self._modified_import_files.items():
|
|
860
|
+
if file_path.exists():
|
|
861
|
+
try:
|
|
862
|
+
file_path.write_text(original_content, encoding="utf-8")
|
|
863
|
+
print(f"Restored original imports in: {file_path}")
|
|
864
|
+
except Exception as e:
|
|
865
|
+
print(
|
|
866
|
+
f"Warning: Could not restore imports in {file_path}: {e}",
|
|
867
|
+
file=sys.stderr,
|
|
868
|
+
)
|
|
869
|
+
self._modified_import_files.clear()
|
|
870
|
+
|
|
711
871
|
# Remove all .egg-info directories in src_dir and project_root
|
|
712
872
|
self._cleanup_egg_info_dirs()
|
|
713
873
|
|
|
@@ -141,8 +141,9 @@ class SubfolderBuildConfig:
|
|
|
141
141
|
result = []
|
|
142
142
|
in_hatch_build = False
|
|
143
143
|
in_hatch_build_section = False
|
|
144
|
+
in_sdist_section = False
|
|
144
145
|
packages_set = False
|
|
145
|
-
|
|
146
|
+
only_include_set = False
|
|
146
147
|
|
|
147
148
|
for line in lines:
|
|
148
149
|
# Detect hatch build section
|
|
@@ -165,10 +166,20 @@ class SubfolderBuildConfig:
|
|
|
165
166
|
# End of hatch build section
|
|
166
167
|
in_hatch_build_section = False
|
|
167
168
|
result.append(line)
|
|
169
|
+
elif line.strip().startswith("[tool.hatch.build.targets.sdist]"):
|
|
170
|
+
in_sdist_section = True
|
|
171
|
+
result.append(line)
|
|
172
|
+
continue
|
|
173
|
+
elif line.strip().startswith("[") and in_sdist_section:
|
|
174
|
+
# End of sdist section
|
|
175
|
+
in_sdist_section = False
|
|
176
|
+
result.append(line)
|
|
177
|
+
elif in_sdist_section:
|
|
178
|
+
# Track if only-include already exists
|
|
179
|
+
if re.match(r"^\s*only-include\s*=", line):
|
|
180
|
+
only_include_set = True
|
|
181
|
+
result.append(line)
|
|
168
182
|
elif in_hatch_build_section:
|
|
169
|
-
# Track if paths-exclude already exists
|
|
170
|
-
if re.match(r"^\s*paths-exclude\s*=", line):
|
|
171
|
-
paths_exclude_set = True
|
|
172
183
|
result.append(line)
|
|
173
184
|
elif in_hatch_build:
|
|
174
185
|
# Modify packages path if found
|
|
@@ -209,42 +220,21 @@ class SubfolderBuildConfig:
|
|
|
209
220
|
packages_str = f'"{correct_packages_path}"'
|
|
210
221
|
result.append(f"packages = [{packages_str}]")
|
|
211
222
|
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
if not
|
|
223
|
+
# Use only-include for source distributions to ensure only the subfolder is included
|
|
224
|
+
# This prevents including files from the project root
|
|
225
|
+
if correct_packages_path and not only_include_set:
|
|
215
226
|
result.append("")
|
|
216
|
-
result.append("[tool.hatch.build]")
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
result.append(
|
|
227
|
-
result.append(' "tests/**",')
|
|
228
|
-
result.append(' "test/**",')
|
|
229
|
-
result.append(' "dist/**",')
|
|
230
|
-
result.append(' "build/**",')
|
|
231
|
-
result.append(' "*.egg-info/**",')
|
|
232
|
-
result.append(' "__pycache__/**",')
|
|
233
|
-
result.append(' ".pytest_cache/**",')
|
|
234
|
-
result.append(' ".mypy_cache/**",')
|
|
235
|
-
result.append(' ".venv/**",')
|
|
236
|
-
result.append(' "venv/**",')
|
|
237
|
-
result.append(' ".git/**",')
|
|
238
|
-
result.append(' ".gitignore",')
|
|
239
|
-
result.append(' ".gitattributes",')
|
|
240
|
-
result.append(' "Dockerfile",')
|
|
241
|
-
result.append(' ".dockerignore",')
|
|
242
|
-
result.append(' ".pylintrc",')
|
|
243
|
-
result.append(' "pyrightconfig.json",')
|
|
244
|
-
result.append(' "git-filter-repo",')
|
|
245
|
-
result.append(' "pyproject.toml.original",')
|
|
246
|
-
result.append(' "README.md.backup",')
|
|
247
|
-
result.append("]")
|
|
227
|
+
result.append("[tool.hatch.build.targets.sdist]")
|
|
228
|
+
# Include only the subfolder directory and necessary files
|
|
229
|
+
only_include_paths = [correct_packages_path]
|
|
230
|
+
# Also include pyproject.toml and README if they exist
|
|
231
|
+
only_include_paths.append("pyproject.toml")
|
|
232
|
+
only_include_paths.append("README.md")
|
|
233
|
+
only_include_paths.append("README.rst")
|
|
234
|
+
only_include_paths.append("README.txt")
|
|
235
|
+
only_include_paths.append("README")
|
|
236
|
+
only_include_str = ", ".join(f'"{p}"' for p in only_include_paths)
|
|
237
|
+
result.append(f"only-include = [{only_include_str}]")
|
|
248
238
|
|
|
249
239
|
return "\n".join(result)
|
|
250
240
|
|
|
@@ -584,41 +574,21 @@ class SubfolderBuildConfig:
|
|
|
584
574
|
packages_str = ", ".join(f'"{p}"' for p in package_dirs)
|
|
585
575
|
result.append(f"packages = [{packages_str}]")
|
|
586
576
|
|
|
587
|
-
#
|
|
588
|
-
# This
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
result.append(' "test/**",')
|
|
603
|
-
result.append(' "dist/**",')
|
|
604
|
-
result.append(' "build/**",')
|
|
605
|
-
result.append(' "*.egg-info/**",')
|
|
606
|
-
result.append(' "__pycache__/**",')
|
|
607
|
-
result.append(' ".pytest_cache/**",')
|
|
608
|
-
result.append(' ".mypy_cache/**",')
|
|
609
|
-
result.append(' ".venv/**",')
|
|
610
|
-
result.append(' "venv/**",')
|
|
611
|
-
result.append(' ".git/**",')
|
|
612
|
-
result.append(' ".gitignore",')
|
|
613
|
-
result.append(' ".gitattributes",')
|
|
614
|
-
result.append(' "Dockerfile",')
|
|
615
|
-
result.append(' ".dockerignore",')
|
|
616
|
-
result.append(' ".pylintrc",')
|
|
617
|
-
result.append(' "pyrightconfig.json",')
|
|
618
|
-
result.append(' "git-filter-repo",')
|
|
619
|
-
result.append(' "pyproject.toml.original",')
|
|
620
|
-
result.append(' "README.md.backup",')
|
|
621
|
-
result.append("]")
|
|
577
|
+
# Use only-include for source distributions to ensure only the subfolder is included
|
|
578
|
+
# This prevents including files from the project root
|
|
579
|
+
if package_dirs:
|
|
580
|
+
result.append("")
|
|
581
|
+
result.append("[tool.hatch.build.targets.sdist]")
|
|
582
|
+
# Include only the subfolder directory and necessary files
|
|
583
|
+
only_include_paths = [package_dirs[0]]
|
|
584
|
+
# Also include pyproject.toml and README if they exist
|
|
585
|
+
only_include_paths.append("pyproject.toml")
|
|
586
|
+
only_include_paths.append("README.md")
|
|
587
|
+
only_include_paths.append("README.rst")
|
|
588
|
+
only_include_paths.append("README.txt")
|
|
589
|
+
only_include_paths.append("README")
|
|
590
|
+
only_include_str = ", ".join(f'"{p}"' for p in only_include_paths)
|
|
591
|
+
result.append(f"only-include = [{only_include_str}]")
|
|
622
592
|
|
|
623
593
|
# Add dependency group if specified
|
|
624
594
|
if dependency_group:
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_build_with_external_deps.py
RENAMED
|
@@ -363,6 +363,94 @@ class TestBuildManager:
|
|
|
363
363
|
assert len(manager.copied_files) == 0
|
|
364
364
|
assert len(manager.copied_dirs) == 0
|
|
365
365
|
|
|
366
|
+
def test_convert_copied_dependency_imports_to_relative(self, tmp_path: Path) -> None:
|
|
367
|
+
"""Test that absolute imports of copied dependencies are converted to relative imports for subfolder builds."""
|
|
368
|
+
project_root = tmp_path / "test_project"
|
|
369
|
+
project_root.mkdir()
|
|
370
|
+
|
|
371
|
+
# Create pyproject.toml
|
|
372
|
+
(project_root / "pyproject.toml").write_text(
|
|
373
|
+
"""[project]
|
|
374
|
+
name = "test-package"
|
|
375
|
+
version = "0.1.0"
|
|
376
|
+
"""
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Create external dependency _shared
|
|
380
|
+
shared_dir = project_root / "_shared"
|
|
381
|
+
shared_dir.mkdir()
|
|
382
|
+
(shared_dir / "__init__.py").write_text("")
|
|
383
|
+
(shared_dir / "image_utils.py").write_text("def save_PIL_image(): pass")
|
|
384
|
+
(shared_dir / "file_utils.py").write_text("def get_filepaths_config(): pass")
|
|
385
|
+
|
|
386
|
+
# Create external dependency _globals
|
|
387
|
+
(project_root / "_globals.py").write_text("is_testing = False")
|
|
388
|
+
|
|
389
|
+
# Create subfolder to build
|
|
390
|
+
subfolder = project_root / "src" / "integration" / "empty_drawing_detection"
|
|
391
|
+
subfolder.mkdir(parents=True)
|
|
392
|
+
(subfolder / "__init__.py").write_text("")
|
|
393
|
+
|
|
394
|
+
# Create a file in subfolder that imports copied dependencies with absolute imports
|
|
395
|
+
test_file = subfolder / "detect_empty_drawings.py"
|
|
396
|
+
original_content = """from pathlib import Path
|
|
397
|
+
from _shared.image_utils import save_PIL_image
|
|
398
|
+
from _shared.file_utils import get_filepaths_config
|
|
399
|
+
from _globals import is_testing
|
|
400
|
+
|
|
401
|
+
def analyze_folder():
|
|
402
|
+
save_PIL_image()
|
|
403
|
+
get_filepaths_config()
|
|
404
|
+
return is_testing
|
|
405
|
+
"""
|
|
406
|
+
test_file.write_text(original_content)
|
|
407
|
+
|
|
408
|
+
# Create another file with different import style
|
|
409
|
+
test_file2 = subfolder / "config.py"
|
|
410
|
+
original_content2 = """import _globals
|
|
411
|
+
|
|
412
|
+
def get_config():
|
|
413
|
+
return _globals.is_testing
|
|
414
|
+
"""
|
|
415
|
+
test_file2.write_text(original_content2)
|
|
416
|
+
|
|
417
|
+
manager = BuildManager(project_root, subfolder)
|
|
418
|
+
|
|
419
|
+
try:
|
|
420
|
+
# Prepare build - this should copy dependencies and convert imports
|
|
421
|
+
external_deps = manager.prepare_build(version="1.0.0", package_name="test-package")
|
|
422
|
+
|
|
423
|
+
# Verify dependencies were copied
|
|
424
|
+
assert len(external_deps) >= 2
|
|
425
|
+
assert (subfolder / "_shared").exists()
|
|
426
|
+
assert (subfolder / "_globals.py").exists()
|
|
427
|
+
|
|
428
|
+
# Verify imports were converted to relative
|
|
429
|
+
modified_content = test_file.read_text()
|
|
430
|
+
assert "from ._shared.image_utils import save_PIL_image" in modified_content
|
|
431
|
+
assert "from ._shared.file_utils import get_filepaths_config" in modified_content
|
|
432
|
+
assert "from ._globals import is_testing" in modified_content
|
|
433
|
+
# Verify stdlib import was not changed
|
|
434
|
+
assert "from pathlib import Path" in modified_content
|
|
435
|
+
|
|
436
|
+
# Verify import statement conversion
|
|
437
|
+
modified_content2 = test_file2.read_text()
|
|
438
|
+
assert "from . import _globals" in modified_content2
|
|
439
|
+
|
|
440
|
+
# Cleanup should restore original imports
|
|
441
|
+
manager.cleanup()
|
|
442
|
+
|
|
443
|
+
restored_content = test_file.read_text()
|
|
444
|
+
assert restored_content == original_content
|
|
445
|
+
|
|
446
|
+
restored_content2 = test_file2.read_text()
|
|
447
|
+
assert restored_content2 == original_content2
|
|
448
|
+
|
|
449
|
+
finally:
|
|
450
|
+
# Ensure cleanup even if test fails
|
|
451
|
+
if manager._modified_import_files:
|
|
452
|
+
manager.cleanup()
|
|
453
|
+
|
|
366
454
|
|
|
367
455
|
class TestRealFolderStructure:
|
|
368
456
|
"""Tests using the real folder_structure from tests directory."""
|
|
@@ -711,30 +711,24 @@ class TestSubfolderBuildTemporaryPyprojectCreation:
|
|
|
711
711
|
assert pyproject_path.exists()
|
|
712
712
|
content = pyproject_path.read_text()
|
|
713
713
|
|
|
714
|
-
# Verify [tool.hatch.build] section exists
|
|
715
|
-
assert "[tool.hatch.build]" in content
|
|
716
|
-
|
|
717
|
-
# Verify
|
|
718
|
-
assert "
|
|
719
|
-
|
|
720
|
-
# Verify
|
|
721
|
-
assert '"
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
assert '"
|
|
725
|
-
assert '"
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
assert '"
|
|
729
|
-
assert '"
|
|
730
|
-
assert '"
|
|
731
|
-
assert '"
|
|
732
|
-
assert '"*.egg-info/**"' in content
|
|
733
|
-
assert '"__pycache__/**"' in content
|
|
734
|
-
assert '".pytest_cache/**"' in content
|
|
735
|
-
assert '".git/**"' in content
|
|
736
|
-
assert '"Dockerfile"' in content
|
|
737
|
-
assert '".gitignore"' in content
|
|
714
|
+
# Verify [tool.hatch.build.targets.sdist] section exists
|
|
715
|
+
assert "[tool.hatch.build.targets.sdist]" in content
|
|
716
|
+
|
|
717
|
+
# Verify only-include is present
|
|
718
|
+
assert "only-include = [" in content
|
|
719
|
+
|
|
720
|
+
# Verify the subfolder is included
|
|
721
|
+
assert '"subfolder"' in content
|
|
722
|
+
|
|
723
|
+
# Verify necessary files are included
|
|
724
|
+
assert '"pyproject.toml"' in content
|
|
725
|
+
assert '"README.md"' in content
|
|
726
|
+
|
|
727
|
+
# Verify non-package directories are NOT explicitly included
|
|
728
|
+
assert '".cursor"' not in content or '".cursor"' not in content.split("only-include")[1]
|
|
729
|
+
assert '".github"' not in content or '".github"' not in content.split("only-include")[1]
|
|
730
|
+
assert '"data"' not in content or '"data"' not in content.split("only-include")[1]
|
|
731
|
+
assert '"docs"' not in content or '"docs"' not in content.split("only-include")[1]
|
|
738
732
|
|
|
739
733
|
# Cleanup
|
|
740
734
|
config.restore()
|
|
@@ -771,18 +765,17 @@ description = "Subfolder package"
|
|
|
771
765
|
assert pyproject_path.exists()
|
|
772
766
|
content = pyproject_path.read_text()
|
|
773
767
|
|
|
774
|
-
# Verify [tool.hatch.build] section exists
|
|
775
|
-
assert "[tool.hatch.build]" in content
|
|
768
|
+
# Verify [tool.hatch.build.targets.sdist] section exists
|
|
769
|
+
assert "[tool.hatch.build.targets.sdist]" in content
|
|
770
|
+
|
|
771
|
+
# Verify only-include is present
|
|
772
|
+
assert "only-include = [" in content
|
|
776
773
|
|
|
777
|
-
# Verify
|
|
778
|
-
assert "
|
|
774
|
+
# Verify the subfolder is included
|
|
775
|
+
assert '"subfolder-package"' in content or '"subfolder"' in content
|
|
779
776
|
|
|
780
|
-
# Verify
|
|
781
|
-
assert '".
|
|
782
|
-
assert '"data/**"' in content
|
|
783
|
-
assert '"docs/**"' in content
|
|
784
|
-
assert '"dist/**"' in content
|
|
785
|
-
assert '"build/**"' in content
|
|
777
|
+
# Verify necessary files are included
|
|
778
|
+
assert '"pyproject.toml"' in content
|
|
786
779
|
|
|
787
780
|
# Cleanup
|
|
788
781
|
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
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/finder.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/publisher.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/utils.py
RENAMED
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-3.1.0 → python_package_folder-3.1.2}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|