python-package-folder 6.0.0__py3-none-any.whl → 7.0.0__py3-none-any.whl
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/analyzer.py +16 -5
- python_package_folder/subfolder_build.py +144 -77
- {python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/METADATA +1 -1
- {python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/RECORD +7 -7
- {python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/WHEEL +0 -0
- {python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/entry_points.txt +0 -0
- {python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -227,9 +227,22 @@ class ImportAnalyzer:
|
|
|
227
227
|
import_info.classification = "stdlib"
|
|
228
228
|
return
|
|
229
229
|
|
|
230
|
+
# Check if it's a third-party package (in site-packages) FIRST
|
|
231
|
+
# This must be checked before resolve_local_import to avoid incorrectly
|
|
232
|
+
# classifying site-packages modules as "external" when they're found
|
|
233
|
+
# by the recursive search
|
|
234
|
+
if self.is_third_party(module_name):
|
|
235
|
+
import_info.classification = "third_party"
|
|
236
|
+
return
|
|
237
|
+
|
|
230
238
|
# Try to resolve as a local import
|
|
231
239
|
resolved = self.resolve_local_import(import_info, src_dir)
|
|
232
240
|
if resolved is not None:
|
|
241
|
+
# Double-check: if resolved path is in site-packages, it's actually third-party
|
|
242
|
+
# (this can happen if the recursive search finds it before importlib does)
|
|
243
|
+
if "site-packages" in str(resolved) or "dist-packages" in str(resolved):
|
|
244
|
+
import_info.classification = "third_party"
|
|
245
|
+
return
|
|
233
246
|
if resolved.is_relative_to(src_dir):
|
|
234
247
|
import_info.classification = "local"
|
|
235
248
|
else:
|
|
@@ -237,11 +250,6 @@ class ImportAnalyzer:
|
|
|
237
250
|
import_info.resolved_path = resolved
|
|
238
251
|
return
|
|
239
252
|
|
|
240
|
-
# Check if it's a third-party package (in site-packages)
|
|
241
|
-
if self.is_third_party(module_name):
|
|
242
|
-
import_info.classification = "third_party"
|
|
243
|
-
return
|
|
244
|
-
|
|
245
253
|
# Mark as ambiguous if we can't determine
|
|
246
254
|
import_info.classification = "ambiguous"
|
|
247
255
|
|
|
@@ -341,6 +349,9 @@ class ImportAnalyzer:
|
|
|
341
349
|
# Only search within project_root to avoid going too far
|
|
342
350
|
if not potential_file.is_relative_to(self.project_root):
|
|
343
351
|
continue
|
|
352
|
+
# Skip site-packages and dist-packages (these are third-party, not external)
|
|
353
|
+
if "site-packages" in str(potential_file) or "dist-packages" in str(potential_file):
|
|
354
|
+
continue
|
|
344
355
|
# Skip excluded patterns
|
|
345
356
|
if any(
|
|
346
357
|
part.startswith("_SS")
|
|
@@ -130,8 +130,8 @@ class SubfolderBuildConfig:
|
|
|
130
130
|
The package name (with hyphens) is converted to the import name (with underscores).
|
|
131
131
|
For example: 'ml-drawing-assistant-data' -> 'ml_drawing_assistant_data'
|
|
132
132
|
|
|
133
|
-
The temporary directory is created in the project root
|
|
134
|
-
|
|
133
|
+
The temporary directory is created in the project root with the import name directly.
|
|
134
|
+
This way, hatchling will install it with the correct name without needing force-include.
|
|
135
135
|
"""
|
|
136
136
|
if not self.package_name:
|
|
137
137
|
return
|
|
@@ -140,20 +140,42 @@ class SubfolderBuildConfig:
|
|
|
140
140
|
# PyPI package names use hyphens, but Python import names use underscores
|
|
141
141
|
import_name = self.package_name.replace("-", "_")
|
|
142
142
|
|
|
143
|
-
# Create temporary directory name
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
# Create temporary directory with the import name directly
|
|
144
|
+
# This way, hatchling will install it with the correct name
|
|
145
|
+
import_name_dir = self.project_root / import_name
|
|
146
146
|
|
|
147
|
-
#
|
|
148
|
-
if
|
|
149
|
-
|
|
147
|
+
# Check if the directory already exists and is the correct one
|
|
148
|
+
if import_name_dir.exists() and import_name_dir == self._temp_package_dir:
|
|
149
|
+
# Directory already exists and is the correct one, no need to recreate
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
# Remove if it already exists (from a previous build)
|
|
153
|
+
if import_name_dir.exists():
|
|
154
|
+
shutil.rmtree(import_name_dir)
|
|
155
|
+
|
|
156
|
+
# Copy the entire source directory contents directly to the import name directory
|
|
157
|
+
# Check if src_dir exists and is a directory before copying
|
|
158
|
+
if not self.src_dir.exists():
|
|
159
|
+
print(
|
|
160
|
+
f"Warning: Source directory does not exist: {self.src_dir}",
|
|
161
|
+
file=sys.stderr,
|
|
162
|
+
)
|
|
163
|
+
self._temp_package_dir = None
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
if not self.src_dir.is_dir():
|
|
167
|
+
print(
|
|
168
|
+
f"Warning: Source path is not a directory: {self.src_dir}",
|
|
169
|
+
file=sys.stderr,
|
|
170
|
+
)
|
|
171
|
+
self._temp_package_dir = None
|
|
172
|
+
return
|
|
150
173
|
|
|
151
|
-
# Copy the entire source directory contents to the temporary directory
|
|
152
174
|
try:
|
|
153
|
-
shutil.copytree(self.src_dir,
|
|
154
|
-
self._temp_package_dir =
|
|
175
|
+
shutil.copytree(self.src_dir, import_name_dir)
|
|
176
|
+
self._temp_package_dir = import_name_dir
|
|
155
177
|
print(
|
|
156
|
-
f"Created temporary package directory: {
|
|
178
|
+
f"Created temporary package directory: {import_name_dir} "
|
|
157
179
|
f"(import name: {import_name})"
|
|
158
180
|
)
|
|
159
181
|
except Exception as e:
|
|
@@ -344,6 +366,116 @@ class SubfolderBuildConfig:
|
|
|
344
366
|
if not self.version:
|
|
345
367
|
raise ValueError("Version is required for subfolder builds")
|
|
346
368
|
|
|
369
|
+
# Check if pyproject.toml exists in subfolder FIRST
|
|
370
|
+
# This allows us to handle subfolder pyproject.toml even when parent doesn't exist
|
|
371
|
+
# But first ensure src_dir exists
|
|
372
|
+
if not self.src_dir.exists() or not self.src_dir.is_dir():
|
|
373
|
+
# If src_dir doesn't exist, we can't proceed
|
|
374
|
+
print(
|
|
375
|
+
f"Warning: Source directory does not exist or is not a directory: {self.src_dir}",
|
|
376
|
+
file=sys.stderr,
|
|
377
|
+
)
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
subfolder_pyproject = self.src_dir / "pyproject.toml"
|
|
381
|
+
if subfolder_pyproject.exists() and subfolder_pyproject.is_file():
|
|
382
|
+
# Read the subfolder pyproject.toml content IMMEDIATELY after checking it exists
|
|
383
|
+
# This prevents any issues if the file is affected by subsequent operations
|
|
384
|
+
try:
|
|
385
|
+
subfolder_content = subfolder_pyproject.read_text(encoding="utf-8")
|
|
386
|
+
except (FileNotFoundError, OSError) as e:
|
|
387
|
+
# File was deleted or inaccessible between check and read
|
|
388
|
+
print(
|
|
389
|
+
f"Warning: Could not read subfolder pyproject.toml at {subfolder_pyproject}: {e}. "
|
|
390
|
+
"Falling back to creating from parent.",
|
|
391
|
+
file=sys.stderr,
|
|
392
|
+
)
|
|
393
|
+
subfolder_content = None
|
|
394
|
+
|
|
395
|
+
if subfolder_content is not None:
|
|
396
|
+
# Ensure src_dir is a package (has __init__.py) before creating temp directory
|
|
397
|
+
# This way the __init__.py will be copied to the temp directory
|
|
398
|
+
init_file = self.src_dir / "__init__.py"
|
|
399
|
+
if not init_file.exists():
|
|
400
|
+
# Create a temporary __init__.py to make it a package
|
|
401
|
+
init_file.write_text("# Temporary __init__.py for build\n", encoding="utf-8")
|
|
402
|
+
self._temp_init_created = True
|
|
403
|
+
else:
|
|
404
|
+
self._temp_init_created = False
|
|
405
|
+
|
|
406
|
+
# Create temporary package directory with correct import name
|
|
407
|
+
# This will copy the __init__.py we just created (if any)
|
|
408
|
+
self._create_temp_package_directory()
|
|
409
|
+
|
|
410
|
+
# Determine which directory to use (temp package dir or src_dir)
|
|
411
|
+
package_dir = self._temp_package_dir if self._temp_package_dir and self._temp_package_dir.exists() else self.src_dir
|
|
412
|
+
# Use the subfolder's pyproject.toml
|
|
413
|
+
print(f"Using existing pyproject.toml from subfolder: {subfolder_pyproject}")
|
|
414
|
+
self._used_subfolder_pyproject = True
|
|
415
|
+
|
|
416
|
+
# Store reference to original project root pyproject.toml
|
|
417
|
+
original_pyproject = self.project_root / "pyproject.toml"
|
|
418
|
+
self.original_pyproject_path = original_pyproject
|
|
419
|
+
|
|
420
|
+
# Create temporary pyproject.toml file
|
|
421
|
+
temp_pyproject_path = self.project_root / "pyproject.toml.temp"
|
|
422
|
+
|
|
423
|
+
# Adjust packages path to be relative to project root
|
|
424
|
+
adjusted_content = self._adjust_subfolder_pyproject_packages_path(subfolder_content)
|
|
425
|
+
|
|
426
|
+
# Read exclude patterns from root pyproject.toml and inject them (if it exists)
|
|
427
|
+
exclude_patterns = []
|
|
428
|
+
if original_pyproject.exists():
|
|
429
|
+
exclude_patterns = read_exclude_patterns(original_pyproject)
|
|
430
|
+
print(
|
|
431
|
+
f"INFO: Read exclude patterns from {original_pyproject}: {exclude_patterns}",
|
|
432
|
+
file=sys.stderr,
|
|
433
|
+
)
|
|
434
|
+
else:
|
|
435
|
+
print(
|
|
436
|
+
f"INFO: No parent pyproject.toml found at {original_pyproject}, skipping exclude patterns",
|
|
437
|
+
file=sys.stderr,
|
|
438
|
+
)
|
|
439
|
+
if exclude_patterns:
|
|
440
|
+
adjusted_content = self._inject_exclude_patterns(adjusted_content, exclude_patterns)
|
|
441
|
+
|
|
442
|
+
# Write adjusted content to temporary file
|
|
443
|
+
temp_pyproject_path.write_text(adjusted_content, encoding="utf-8")
|
|
444
|
+
self.temp_pyproject = temp_pyproject_path
|
|
445
|
+
|
|
446
|
+
# Print the temporary pyproject.toml content for debugging
|
|
447
|
+
print("\n" + "=" * 80)
|
|
448
|
+
print("Temporary pyproject.toml content (from subfolder pyproject.toml):")
|
|
449
|
+
print("=" * 80)
|
|
450
|
+
print(adjusted_content)
|
|
451
|
+
print("=" * 80 + "\n")
|
|
452
|
+
|
|
453
|
+
# If original pyproject.toml exists, temporarily move it
|
|
454
|
+
if original_pyproject.exists():
|
|
455
|
+
backup_path = self.project_root / "pyproject.toml.original"
|
|
456
|
+
# Remove backup if it already exists (from previous failed test or run)
|
|
457
|
+
if backup_path.exists():
|
|
458
|
+
backup_path.unlink()
|
|
459
|
+
original_pyproject.rename(backup_path)
|
|
460
|
+
self.original_pyproject_backup = backup_path
|
|
461
|
+
|
|
462
|
+
# Move temp file to pyproject.toml for the build
|
|
463
|
+
temp_pyproject_path.rename(original_pyproject)
|
|
464
|
+
self.temp_pyproject = original_pyproject
|
|
465
|
+
|
|
466
|
+
# Handle README file
|
|
467
|
+
self._handle_readme()
|
|
468
|
+
|
|
469
|
+
# Exclude files matching exclude patterns
|
|
470
|
+
if exclude_patterns:
|
|
471
|
+
self._exclude_files_by_patterns(exclude_patterns)
|
|
472
|
+
|
|
473
|
+
return original_pyproject
|
|
474
|
+
|
|
475
|
+
# No pyproject.toml in subfolder, create one from parent
|
|
476
|
+
self._used_subfolder_pyproject = False
|
|
477
|
+
print("No pyproject.toml found in subfolder, creating temporary one from parent")
|
|
478
|
+
|
|
347
479
|
# Ensure src_dir is a package (has __init__.py) before creating temp directory
|
|
348
480
|
# This way the __init__.py will be copied to the temp directory
|
|
349
481
|
init_file = self.src_dir / "__init__.py"
|
|
@@ -361,71 +493,6 @@ class SubfolderBuildConfig:
|
|
|
361
493
|
# Determine which directory to use (temp package dir or src_dir)
|
|
362
494
|
package_dir = self._temp_package_dir if self._temp_package_dir and self._temp_package_dir.exists() else self.src_dir
|
|
363
495
|
|
|
364
|
-
# Check if pyproject.toml exists in subfolder
|
|
365
|
-
subfolder_pyproject = self.src_dir / "pyproject.toml"
|
|
366
|
-
if subfolder_pyproject.exists():
|
|
367
|
-
# Use the subfolder's pyproject.toml
|
|
368
|
-
print(f"Using existing pyproject.toml from subfolder: {subfolder_pyproject}")
|
|
369
|
-
self._used_subfolder_pyproject = True
|
|
370
|
-
|
|
371
|
-
# Store reference to original project root pyproject.toml
|
|
372
|
-
original_pyproject = self.project_root / "pyproject.toml"
|
|
373
|
-
self.original_pyproject_path = original_pyproject
|
|
374
|
-
|
|
375
|
-
# Create temporary pyproject.toml file
|
|
376
|
-
temp_pyproject_path = self.project_root / "pyproject.toml.temp"
|
|
377
|
-
|
|
378
|
-
# Read and adjust the subfolder pyproject.toml
|
|
379
|
-
subfolder_content = subfolder_pyproject.read_text(encoding="utf-8")
|
|
380
|
-
# Adjust packages path to be relative to project root
|
|
381
|
-
adjusted_content = self._adjust_subfolder_pyproject_packages_path(subfolder_content)
|
|
382
|
-
|
|
383
|
-
# Read exclude patterns from root pyproject.toml and inject them
|
|
384
|
-
exclude_patterns = read_exclude_patterns(original_pyproject)
|
|
385
|
-
print(
|
|
386
|
-
f"INFO: Read exclude patterns from {original_pyproject}: {exclude_patterns}",
|
|
387
|
-
file=sys.stderr,
|
|
388
|
-
)
|
|
389
|
-
if exclude_patterns:
|
|
390
|
-
adjusted_content = self._inject_exclude_patterns(adjusted_content, exclude_patterns)
|
|
391
|
-
|
|
392
|
-
# Write adjusted content to temporary file
|
|
393
|
-
temp_pyproject_path.write_text(adjusted_content, encoding="utf-8")
|
|
394
|
-
self.temp_pyproject = temp_pyproject_path
|
|
395
|
-
|
|
396
|
-
# Print the temporary pyproject.toml content for debugging
|
|
397
|
-
print("\n" + "=" * 80)
|
|
398
|
-
print("Temporary pyproject.toml content (from subfolder pyproject.toml):")
|
|
399
|
-
print("=" * 80)
|
|
400
|
-
print(adjusted_content)
|
|
401
|
-
print("=" * 80 + "\n")
|
|
402
|
-
|
|
403
|
-
# If original pyproject.toml exists, temporarily move it
|
|
404
|
-
if original_pyproject.exists():
|
|
405
|
-
backup_path = self.project_root / "pyproject.toml.original"
|
|
406
|
-
# Remove backup if it already exists (from previous failed test or run)
|
|
407
|
-
if backup_path.exists():
|
|
408
|
-
backup_path.unlink()
|
|
409
|
-
original_pyproject.rename(backup_path)
|
|
410
|
-
self.original_pyproject_backup = backup_path
|
|
411
|
-
|
|
412
|
-
# Move temp file to pyproject.toml for the build
|
|
413
|
-
temp_pyproject_path.rename(original_pyproject)
|
|
414
|
-
self.temp_pyproject = original_pyproject
|
|
415
|
-
|
|
416
|
-
# Handle README file
|
|
417
|
-
self._handle_readme()
|
|
418
|
-
|
|
419
|
-
# Exclude files matching exclude patterns
|
|
420
|
-
if exclude_patterns:
|
|
421
|
-
self._exclude_files_by_patterns(exclude_patterns)
|
|
422
|
-
|
|
423
|
-
return original_pyproject
|
|
424
|
-
|
|
425
|
-
# No pyproject.toml in subfolder, create one from parent
|
|
426
|
-
self._used_subfolder_pyproject = False
|
|
427
|
-
print("No pyproject.toml found in subfolder, creating temporary one from parent")
|
|
428
|
-
|
|
429
496
|
# Read the original pyproject.toml
|
|
430
497
|
original_pyproject = self.project_root / "pyproject.toml"
|
|
431
498
|
if not original_pyproject.exists():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.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>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
python_package_folder/__init__.py,sha256=DQt-uldOEKfh0MUqCvKdeNKOnpuOvpb7blYvXMyO9Wc,719
|
|
2
2
|
python_package_folder/__main__.py,sha256=a-__-VLhYw-J7S7CsHdhtEvQr3RiAZxiYDvKhKTgMX4,291
|
|
3
|
-
python_package_folder/analyzer.py,sha256=
|
|
3
|
+
python_package_folder/analyzer.py,sha256=8DCc5AaVpYF9qh8NbMJ5igeW5AVYnQqy_XqRtl16ZLc,15981
|
|
4
4
|
python_package_folder/finder.py,sha256=RPidZ7LKCFuQ_KgCFIZdHWPXsZIDor3M4C0hKeYW7EI,11799
|
|
5
5
|
python_package_folder/manager.py,sha256=erX8uPu3KA593wsSH9o15O8MNc6mwIZgGGqUJf7Z5i0,59423
|
|
6
6
|
python_package_folder/publisher.py,sha256=fmf3l0zMY9CD49gurxlXyvm9mOP0FzDjmiSt0yDqt1M,18813
|
|
7
7
|
python_package_folder/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
python_package_folder/python_package_folder.py,sha256=QZ-vdOZ40wF-eGbp39JotDhMwIhhT3Z_sC5obQj3i4s,17024
|
|
9
|
-
python_package_folder/subfolder_build.py,sha256=
|
|
9
|
+
python_package_folder/subfolder_build.py,sha256=tkZuaKFNgSTptOVu6v2sjxPfgShUmpMkapZSSyZ4UTk,59465
|
|
10
10
|
python_package_folder/types.py,sha256=3yeSRR5p_3PDKEAaehW_RJ7NwJHexOIeA08bGaT1iSY,2368
|
|
11
11
|
python_package_folder/utils.py,sha256=b6Ukcc0fctXdxS5zhGLS86kqn0vz1yOEK7XjCY9fjfY,5621
|
|
12
12
|
python_package_folder/version.py,sha256=kIDP6S9trEfs9gj7lBYGxrWm4RPssRla24UtlO9Jkh4,9111
|
|
13
13
|
python_package_folder/version_calculator.py,sha256=_gcc8IbMjt27UxePcc7RZlFTMCG3AGnRI_-Mp_4qRG0,39568
|
|
14
|
-
python_package_folder-
|
|
15
|
-
python_package_folder-
|
|
16
|
-
python_package_folder-
|
|
17
|
-
python_package_folder-
|
|
18
|
-
python_package_folder-
|
|
14
|
+
python_package_folder-7.0.0.dist-info/METADATA,sha256=mGdHt_HrzSDGj-tBuvwG2axyjGUxoAIZhTLrgPCBPZQ,7838
|
|
15
|
+
python_package_folder-7.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
16
|
+
python_package_folder-7.0.0.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
|
|
17
|
+
python_package_folder-7.0.0.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
|
|
18
|
+
python_package_folder-7.0.0.dist-info/RECORD,,
|
|
File without changes
|
{python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_package_folder-6.0.0.dist-info → python_package_folder-7.0.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|