python-package-folder 8.1.0__tar.gz → 8.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-8.1.0 → python_package_folder-8.2.0}/PKG-INFO +1 -1
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/pyproject.toml +1 -1
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/finder.py +46 -29
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/manager.py +4 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_subfolder_build.py +88 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.copier-answers.yml +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.github/workflows/ci.yml +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.github/workflows/publish.yml +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.gitignore +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/.vscode/settings.json +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/LICENSE +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/MANIFEST.in +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/Makefile +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/README.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/coverage.svg +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/development.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/DEVELOPMENT.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/INSTALLATION.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/PUBLISHING.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/REFERENCE.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/USAGE.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/docs/VERSION_RESOLUTION.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/installation.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/publishing.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/types.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/version.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/version_calculator.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/conftest.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_exclude_patterns.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_linting.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_publisher.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_utils.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_version_calculator.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_version_manager.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/tests.py +0 -0
- {python_package_folder-8.1.0 → python_package_folder-8.2.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.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-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/finder.py
RENAMED
|
@@ -29,18 +29,26 @@ class ExternalDependencyFinder:
|
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
|
-
self,
|
|
32
|
+
self,
|
|
33
|
+
project_root: Path,
|
|
34
|
+
src_dir: Path,
|
|
35
|
+
exclude_patterns: list[str] | None = None,
|
|
36
|
+
original_src_dir: Path | None = None,
|
|
33
37
|
) -> None:
|
|
34
38
|
"""
|
|
35
39
|
Initialize the dependency finder.
|
|
36
40
|
|
|
37
41
|
Args:
|
|
38
42
|
project_root: Root directory of the project
|
|
39
|
-
src_dir: Source directory to analyze
|
|
43
|
+
src_dir: Source directory to analyze (may be temp directory for subfolder builds)
|
|
40
44
|
exclude_patterns: Additional patterns to exclude (default: common sandbox patterns)
|
|
45
|
+
original_src_dir: Original source directory before any changes (e.g., before temp directory creation).
|
|
46
|
+
Used for relative path checks. If not provided, uses src_dir.
|
|
41
47
|
"""
|
|
42
48
|
self.project_root = project_root.resolve()
|
|
43
49
|
self.src_dir = src_dir.resolve()
|
|
50
|
+
# Store original src_dir for relative path checks (important for subfolder builds)
|
|
51
|
+
self.original_src_dir = (original_src_dir or src_dir).resolve()
|
|
44
52
|
self.analyzer = ImportAnalyzer(project_root)
|
|
45
53
|
# Patterns for directories/files to exclude (sandbox, skip, etc.)
|
|
46
54
|
default_patterns = [
|
|
@@ -90,34 +98,43 @@ class ExternalDependencyFinder:
|
|
|
90
98
|
if source_path.is_file():
|
|
91
99
|
parent_dir = source_path.parent
|
|
92
100
|
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
files_are_imported = True # Always true when processing an import
|
|
99
|
-
|
|
100
|
-
# Only copy immediate parent directory, not grandparent directories
|
|
101
|
-
# This prevents copying entire trees like models/Information_extraction
|
|
102
|
-
# when we only need models/Information_extraction/_shared_ie
|
|
103
|
-
should_copy_dir = (
|
|
104
|
-
not self._should_exclude_path(parent_dir)
|
|
105
|
-
and (
|
|
106
|
-
parent_is_package or files_are_imported
|
|
107
|
-
) # Package OR files imported
|
|
108
|
-
and not parent_dir.is_relative_to(self.src_dir)
|
|
109
|
-
and not self.src_dir.is_relative_to(parent_dir)
|
|
110
|
-
and parent_dir != self.project_root
|
|
111
|
-
and parent_dir != self.project_root.parent
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
if should_copy_dir:
|
|
115
|
-
# Copy the directory instead of just the file
|
|
116
|
-
track_path = parent_dir
|
|
117
|
-
source_path = parent_dir
|
|
118
|
-
else:
|
|
119
|
-
# Copy just the file
|
|
101
|
+
# Never copy the src/ directory itself - only copy individual files at its root
|
|
102
|
+
# This prevents copying the entire src/ directory (with all subdirectories)
|
|
103
|
+
# when only a file like _globals.py is needed
|
|
104
|
+
if parent_dir == self.project_root / "src":
|
|
105
|
+
# For files at root of src/, only copy the file, not the directory
|
|
120
106
|
track_path = source_path
|
|
107
|
+
else:
|
|
108
|
+
# Only copy parent directory if:
|
|
109
|
+
# 1. It's a package (has __init__.py), OR
|
|
110
|
+
# 2. Files from it are actually imported (which is the case here)
|
|
111
|
+
# But only copy the immediate parent, not entire directory trees
|
|
112
|
+
parent_is_package = (parent_dir / "__init__.py").exists()
|
|
113
|
+
files_are_imported = True # Always true when processing an import
|
|
114
|
+
|
|
115
|
+
# Only copy immediate parent directory, not grandparent directories
|
|
116
|
+
# This prevents copying entire trees like models/Information_extraction
|
|
117
|
+
# when we only need models/Information_extraction/_shared_ie
|
|
118
|
+
# Use original_src_dir for relative path checks to correctly handle
|
|
119
|
+
# subfolder builds where src_dir may point to temp directory
|
|
120
|
+
should_copy_dir = (
|
|
121
|
+
not self._should_exclude_path(parent_dir)
|
|
122
|
+
and (
|
|
123
|
+
parent_is_package or files_are_imported
|
|
124
|
+
) # Package OR files imported
|
|
125
|
+
and not parent_dir.is_relative_to(self.src_dir)
|
|
126
|
+
and not self.original_src_dir.is_relative_to(parent_dir)
|
|
127
|
+
and parent_dir != self.project_root
|
|
128
|
+
and parent_dir != self.project_root.parent
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if should_copy_dir:
|
|
132
|
+
# Copy the directory instead of just the file
|
|
133
|
+
track_path = parent_dir
|
|
134
|
+
source_path = parent_dir
|
|
135
|
+
else:
|
|
136
|
+
# Copy just the file
|
|
137
|
+
track_path = source_path
|
|
121
138
|
elif source_path.is_dir():
|
|
122
139
|
# Don't copy directories that contain src_dir
|
|
123
140
|
if self.src_dir.is_relative_to(source_path):
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/manager.py
RENAMED
|
@@ -71,6 +71,8 @@ class BuildManager:
|
|
|
71
71
|
src_dir = self.project_root / "src"
|
|
72
72
|
|
|
73
73
|
self.src_dir = Path(src_dir).resolve()
|
|
74
|
+
# Store original src_dir before any changes (e.g., when temp directory is created)
|
|
75
|
+
self.original_src_dir = self.src_dir
|
|
74
76
|
|
|
75
77
|
# Validate source directory
|
|
76
78
|
if not self.src_dir.exists():
|
|
@@ -280,10 +282,12 @@ class BuildManager:
|
|
|
280
282
|
# Update src_dir to point to temp package directory
|
|
281
283
|
self.src_dir = self.subfolder_config._temp_package_dir
|
|
282
284
|
# Recreate finder with updated src_dir so it calculates target paths correctly
|
|
285
|
+
# Pass original_src_dir for relative path checks to prevent copying entire src/ directory
|
|
283
286
|
self.finder = ExternalDependencyFinder(
|
|
284
287
|
self.project_root,
|
|
285
288
|
self.src_dir,
|
|
286
289
|
exclude_patterns=self.exclude_patterns,
|
|
290
|
+
original_src_dir=self.original_src_dir,
|
|
287
291
|
)
|
|
288
292
|
print(
|
|
289
293
|
f"Using temporary package directory for build: {self.src_dir}"
|
|
@@ -1209,6 +1209,94 @@ class TestTemporaryPackageDirectory:
|
|
|
1209
1209
|
|
|
1210
1210
|
config.restore()
|
|
1211
1211
|
|
|
1212
|
+
def test_only_globals_file_copied_not_entire_src_directory(
|
|
1213
|
+
self, test_project_with_pyproject: Path
|
|
1214
|
+
) -> None:
|
|
1215
|
+
"""
|
|
1216
|
+
Test that when a subfolder imports a file from src/ root (like _globals.py),
|
|
1217
|
+
only that file is copied, not the entire src/ directory.
|
|
1218
|
+
|
|
1219
|
+
This is a regression test for the bug where the entire src/ directory
|
|
1220
|
+
(including features/, integration/, docs/, infrastructure/) was being
|
|
1221
|
+
copied when only _globals.py was needed.
|
|
1222
|
+
"""
|
|
1223
|
+
project_root = test_project_with_pyproject
|
|
1224
|
+
subfolder = project_root / "subfolder"
|
|
1225
|
+
|
|
1226
|
+
# Create a file in subfolder that imports _globals
|
|
1227
|
+
(subfolder / "__init__.py").write_text("# Package init")
|
|
1228
|
+
(subfolder / "module.py").write_text(
|
|
1229
|
+
"from _globals import IS_TESTING\n\ndef func(): return IS_TESTING"
|
|
1230
|
+
)
|
|
1231
|
+
|
|
1232
|
+
# Create _globals.py at root of src/ (outside subfolder)
|
|
1233
|
+
src_dir = project_root / "src"
|
|
1234
|
+
src_dir.mkdir(exist_ok=True)
|
|
1235
|
+
(src_dir / "_globals.py").write_text("IS_TESTING = False")
|
|
1236
|
+
|
|
1237
|
+
# Create other directories in src/ that should NOT be copied
|
|
1238
|
+
(src_dir / "features").mkdir()
|
|
1239
|
+
(src_dir / "features" / "__init__.py").write_text("# Features")
|
|
1240
|
+
(src_dir / "features" / "feature.py").write_text("def feature(): pass")
|
|
1241
|
+
|
|
1242
|
+
(src_dir / "integration").mkdir()
|
|
1243
|
+
(src_dir / "integration" / "__init__.py").write_text("# Integration")
|
|
1244
|
+
(src_dir / "integration" / "integration.py").write_text("def integration(): pass")
|
|
1245
|
+
|
|
1246
|
+
(src_dir / "docs").mkdir()
|
|
1247
|
+
(src_dir / "docs" / "readme.md").write_text("# Docs")
|
|
1248
|
+
|
|
1249
|
+
(src_dir / "infrastructure").mkdir()
|
|
1250
|
+
(src_dir / "infrastructure" / "__init__.py").write_text("# Infrastructure")
|
|
1251
|
+
|
|
1252
|
+
# Build the subfolder
|
|
1253
|
+
manager = BuildManager(project_root=project_root, src_dir=subfolder)
|
|
1254
|
+
|
|
1255
|
+
try:
|
|
1256
|
+
external_deps = manager.prepare_build(version="1.0.0", package_name="my-package")
|
|
1257
|
+
|
|
1258
|
+
# Verify _globals.py was found as an external dependency
|
|
1259
|
+
globals_deps = [d for d in external_deps if d.source_path.name == "_globals.py"]
|
|
1260
|
+
assert len(globals_deps) > 0, "_globals.py should be found as an external dependency"
|
|
1261
|
+
|
|
1262
|
+
# Verify the temp package directory exists
|
|
1263
|
+
assert manager.subfolder_config is not None
|
|
1264
|
+
temp_dir = manager.subfolder_config._temp_package_dir
|
|
1265
|
+
assert temp_dir is not None and temp_dir.exists()
|
|
1266
|
+
|
|
1267
|
+
# Verify _globals.py was copied to temp directory
|
|
1268
|
+
assert (temp_dir / "_globals.py").exists(), "_globals.py should be copied to temp directory"
|
|
1269
|
+
|
|
1270
|
+
# Verify other directories from src/ were NOT copied
|
|
1271
|
+
assert not (temp_dir / "features").exists(), (
|
|
1272
|
+
"features/ directory should NOT be copied (not imported)"
|
|
1273
|
+
)
|
|
1274
|
+
assert not (temp_dir / "integration").exists(), (
|
|
1275
|
+
"integration/ directory should NOT be copied (not imported)"
|
|
1276
|
+
)
|
|
1277
|
+
assert not (temp_dir / "docs").exists(), (
|
|
1278
|
+
"docs/ directory should NOT be copied (not imported)"
|
|
1279
|
+
)
|
|
1280
|
+
assert not (temp_dir / "infrastructure").exists(), (
|
|
1281
|
+
"infrastructure/ directory should NOT be copied (not imported)"
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
# Verify only _globals.py and subfolder contents are in temp directory
|
|
1285
|
+
all_items = list(temp_dir.iterdir())
|
|
1286
|
+
item_names = [item.name for item in all_items]
|
|
1287
|
+
|
|
1288
|
+
# Should have _globals.py, __init__.py, module.py, and possibly pyproject.toml
|
|
1289
|
+
# But NOT features/, integration/, docs/, infrastructure/
|
|
1290
|
+
unexpected_dirs = {"features", "integration", "docs", "infrastructure"}
|
|
1291
|
+
found_unexpected = unexpected_dirs.intersection(set(item_names))
|
|
1292
|
+
assert len(found_unexpected) == 0, (
|
|
1293
|
+
f"Found unexpected directories in temp package: {found_unexpected}. "
|
|
1294
|
+
f"Only _globals.py should be copied, not the entire src/ directory."
|
|
1295
|
+
)
|
|
1296
|
+
|
|
1297
|
+
finally:
|
|
1298
|
+
manager.cleanup()
|
|
1299
|
+
|
|
1212
1300
|
|
|
1213
1301
|
class TestWheelPackaging:
|
|
1214
1302
|
"""Tests to verify that wheels are correctly packaged with the right directory structure."""
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/publisher.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/utils.py
RENAMED
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/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-8.1.0 → python_package_folder-8.2.0}/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
|
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-8.1.0 → python_package_folder-8.2.0}/tests/test_version_calculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|