python-package-folder 7.0.0__tar.gz → 7.1.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.
Files changed (60) hide show
  1. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/PKG-INFO +1 -1
  2. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/pyproject.toml +1 -1
  3. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/manager.py +50 -0
  4. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_subfolder_build.py +113 -1
  5. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.copier-answers.yml +0 -0
  6. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
  7. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
  8. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.cursor/rules/general.mdc +0 -0
  9. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.cursor/rules/python.mdc +0 -0
  10. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.github/workflows/ci.yml +0 -0
  11. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.github/workflows/publish.yml +0 -0
  12. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.gitignore +0 -0
  13. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/.vscode/settings.json +0 -0
  14. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/LICENSE +0 -0
  15. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/MANIFEST.in +0 -0
  16. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/Makefile +0 -0
  17. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/README.md +0 -0
  18. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/coverage.svg +0 -0
  19. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/development.md +0 -0
  20. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/DEVELOPMENT.md +0 -0
  21. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/INSTALLATION.md +0 -0
  22. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/PUBLISHING.md +0 -0
  23. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/REFERENCE.md +0 -0
  24. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/USAGE.md +0 -0
  25. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/docs/VERSION_RESOLUTION.md +0 -0
  26. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/installation.md +0 -0
  27. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/publishing.md +0 -0
  28. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/__init__.py +0 -0
  29. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/__main__.py +0 -0
  30. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/analyzer.py +0 -0
  31. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/finder.py +0 -0
  32. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/publisher.py +0 -0
  33. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/py.typed +0 -0
  34. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/python_package_folder.py +0 -0
  35. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/subfolder_build.py +0 -0
  36. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/types.py +0 -0
  37. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/utils.py +0 -0
  38. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/version.py +0 -0
  39. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/src/python_package_folder/version_calculator.py +0 -0
  40. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/conftest.py +0 -0
  41. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/some_globals.py +0 -0
  42. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/subfolder_to_build/README.md +0 -0
  43. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
  44. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
  45. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
  46. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
  47. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/folder_structure/utility_folder/some_utility.py +0 -0
  48. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_build_with_external_deps.py +0 -0
  49. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_exclude_patterns.py +0 -0
  50. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_linting.py +0 -0
  51. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_preserve_directory_structure.py +0 -0
  52. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_publisher.py +0 -0
  53. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_shared_subdirectory_imports.py +0 -0
  54. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_spreadsheet_creation_imports.py +0 -0
  55. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_third_party_dependencies.py +0 -0
  56. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_utils.py +0 -0
  57. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_version_calculator.py +0 -0
  58. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/test_version_manager.py +0 -0
  59. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/tests/tests.py +0 -0
  60. {python_package_folder-7.0.0 → python_package_folder-7.1.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-package-folder
3
- Version: 7.0.0
3
+ Version: 7.1.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>
@@ -43,7 +43,7 @@ dependencies = [
43
43
 
44
44
  # ---- Dev dependencies ----
45
45
 
46
- version = "7.0.0"
46
+ version = "7.1.0"
47
47
  [dependency-groups]
48
48
  dev = [
49
49
  "pytest>=8.3.5",
@@ -1142,6 +1142,26 @@ class BuildManager:
1142
1142
  else:
1143
1143
  print("No external dependencies found\n")
1144
1144
 
1145
+ # Verify temporary package directory exists if using subfolder build
1146
+ if self.subfolder_config and self.subfolder_config._temp_package_dir:
1147
+ temp_dir = self.subfolder_config._temp_package_dir
1148
+ if not temp_dir.exists():
1149
+ raise RuntimeError(
1150
+ f"Temporary package directory does not exist: {temp_dir}. "
1151
+ "This should have been created during prepare_build()."
1152
+ )
1153
+ # Verify it contains Python files
1154
+ py_files = list(temp_dir.glob("*.py"))
1155
+ if not py_files:
1156
+ raise RuntimeError(
1157
+ f"Temporary package directory exists but contains no Python files: {temp_dir}"
1158
+ )
1159
+ # Verify __init__.py exists
1160
+ if not (temp_dir / "__init__.py").exists():
1161
+ raise RuntimeError(
1162
+ f"Temporary package directory missing __init__.py: {temp_dir}"
1163
+ )
1164
+
1145
1165
  print("Running build...")
1146
1166
  # Build command should run from project root to find pyproject.toml
1147
1167
  import os
@@ -1152,6 +1172,36 @@ class BuildManager:
1152
1172
  build_command()
1153
1173
  finally:
1154
1174
  os.chdir(original_cwd)
1175
+
1176
+ # Verify wheel contents after build (for subfolder builds)
1177
+ if self.subfolder_config and self.subfolder_config.package_name:
1178
+ import_name = self.subfolder_config.package_name.replace("-", "_")
1179
+ dist_dir = self.project_root / "dist"
1180
+ if dist_dir.exists():
1181
+ wheel_files = list(dist_dir.glob("*.whl"))
1182
+ if wheel_files:
1183
+ import zipfile
1184
+ wheel_file = wheel_files[0]
1185
+ try:
1186
+ with zipfile.ZipFile(wheel_file, "r") as wheel:
1187
+ file_names = wheel.namelist()
1188
+ package_files = [f for f in file_names if f.startswith(f"{import_name}/")]
1189
+ if not package_files:
1190
+ print(
1191
+ f"\nWARNING: Built wheel does not contain package directory '{import_name}/'. "
1192
+ f"Only found: {[f for f in file_names if '.dist-info' not in f][:10]}",
1193
+ file=sys.stderr,
1194
+ )
1195
+ else:
1196
+ print(
1197
+ f"\nVerified wheel contains package directory '{import_name}/' "
1198
+ f"with {len(package_files)} files"
1199
+ )
1200
+ except Exception as e:
1201
+ print(
1202
+ f"\nWARNING: Could not verify wheel contents: {e}",
1203
+ file=sys.stderr,
1204
+ )
1155
1205
 
1156
1206
  finally:
1157
1207
  print("\nCleaning up copied files...")
@@ -3,6 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import subprocess
6
+ import sys
7
+ import tempfile
8
+ import venv
6
9
  import zipfile
7
10
  from pathlib import Path
8
11
 
@@ -1241,4 +1244,113 @@ build-backend = "hatchling.build"
1241
1244
  data_files = [f for f in file_names if f.startswith("data/") and ".dist-info" not in f]
1242
1245
  assert len(data_files) == 0, (
1243
1246
  f"Wheel should not contain 'data/' directory, should use '{import_name}/' instead"
1244
- )
1247
+ )
1248
+
1249
+ def test_wheel_installs_with_correct_package_directory(self, tmp_path: Path) -> None:
1250
+ """Test that a built wheel can be installed and the package directory exists."""
1251
+ project_root = tmp_path / "test_project"
1252
+ project_root.mkdir()
1253
+
1254
+ # Create pyproject.toml
1255
+ pyproject_content = """[project]
1256
+ name = "test-package"
1257
+ version = "0.1.0"
1258
+
1259
+ [build-system]
1260
+ requires = ["hatchling"]
1261
+ build-backend = "hatchling.build"
1262
+ """
1263
+ (project_root / "pyproject.toml").write_text(pyproject_content)
1264
+
1265
+ # Create subfolder with package name that has hyphens
1266
+ subfolder = project_root / "src" / "data"
1267
+ subfolder.mkdir(parents=True)
1268
+
1269
+ # Create some Python files
1270
+ (subfolder / "__init__.py").write_text("# Package init")
1271
+ (subfolder / "module.py").write_text("def hello(): return 'world'")
1272
+ (subfolder / "utils.py").write_text("def util(): return 'helper'")
1273
+
1274
+ # Package name with hyphens (like ml-drawing-assistant-data)
1275
+ package_name = "ml-drawing-assistant-data"
1276
+ import_name = "ml_drawing_assistant_data" # Expected import name
1277
+ version = "1.0.0"
1278
+
1279
+ # Build the wheel
1280
+ manager = BuildManager(project_root=project_root, src_dir=subfolder)
1281
+
1282
+ def build_wheel() -> None:
1283
+ """Build the wheel using uv build."""
1284
+ subprocess.run(
1285
+ ["uv", "build", "--wheel"],
1286
+ cwd=project_root,
1287
+ check=True,
1288
+ capture_output=True,
1289
+ )
1290
+
1291
+ try:
1292
+ manager.run_build(build_wheel, version=version, package_name=package_name)
1293
+ finally:
1294
+ manager.cleanup()
1295
+
1296
+ # Find the built wheel
1297
+ dist_dir = project_root / "dist"
1298
+ assert dist_dir.exists(), "dist directory should exist after build"
1299
+
1300
+ wheel_files = list(dist_dir.glob("*.whl"))
1301
+ assert len(wheel_files) > 0, "At least one wheel should be built"
1302
+
1303
+ wheel_file = wheel_files[0]
1304
+
1305
+ # Create a temporary virtual environment and install the wheel
1306
+ venv_dir = tmp_path / "test_venv"
1307
+ venv.create(venv_dir, with_pip=True)
1308
+
1309
+ # Determine the Python executable in the venv
1310
+ if sys.platform == "win32":
1311
+ python_exe = venv_dir / "Scripts" / "python.exe"
1312
+ pip_exe = venv_dir / "Scripts" / "pip.exe"
1313
+ else:
1314
+ python_exe = venv_dir / "bin" / "python"
1315
+ pip_exe = venv_dir / "bin" / "pip"
1316
+
1317
+ # Install the wheel
1318
+ install_result = subprocess.run(
1319
+ [str(pip_exe), "install", str(wheel_file)],
1320
+ capture_output=True,
1321
+ text=True,
1322
+ check=True,
1323
+ )
1324
+
1325
+ # Find the site-packages directory
1326
+ if sys.platform == "win32":
1327
+ site_packages = venv_dir / "Lib" / "site-packages"
1328
+ else:
1329
+ # Get Python version
1330
+ version_result = subprocess.run(
1331
+ [str(python_exe), "-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"],
1332
+ capture_output=True,
1333
+ text=True,
1334
+ check=True,
1335
+ )
1336
+ py_version = version_result.stdout.strip()
1337
+ site_packages = venv_dir / "lib" / f"python{py_version}" / "site-packages"
1338
+
1339
+ assert site_packages.exists(), f"site-packages directory should exist at {site_packages}"
1340
+
1341
+ # Verify the package directory exists (not just dist-info)
1342
+ package_dir = site_packages / import_name
1343
+ assert package_dir.exists(), (
1344
+ f"Package directory {import_name}/ should exist in site-packages after installation. "
1345
+ f"Found in site-packages: {list(site_packages.iterdir())[:20]}"
1346
+ )
1347
+ assert package_dir.is_dir(), f"{import_name} should be a directory, not a file"
1348
+
1349
+ # Verify the expected files are present
1350
+ assert (package_dir / "__init__.py").exists(), f"{import_name}/__init__.py should exist"
1351
+ assert (package_dir / "module.py").exists(), f"{import_name}/module.py should exist"
1352
+ assert (package_dir / "utils.py").exists(), f"{import_name}/utils.py should exist"
1353
+
1354
+ # Verify dist-info also exists
1355
+ dist_info_dir = site_packages / f"{import_name}-{version}.dist-info"
1356
+ assert dist_info_dir.exists(), f"dist-info directory should exist: {dist_info_dir}"