python-package-folder 2.0.2__py3-none-any.whl → 2.0.4__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/manager.py +101 -7
- python_package_folder/subfolder_build.py +26 -4
- {python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/METADATA +1 -1
- {python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/RECORD +7 -7
- {python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/WHEEL +0 -0
- {python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/entry_points.txt +0 -0
- {python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/licenses/LICENSE +0 -0
python_package_folder/manager.py
CHANGED
|
@@ -394,6 +394,88 @@ class BuildManager:
|
|
|
394
394
|
elif src_item.is_dir():
|
|
395
395
|
self._copytree_excluding(src_item, dst_item)
|
|
396
396
|
|
|
397
|
+
def _get_package_name_from_import(self, module_name: str) -> str | None:
|
|
398
|
+
"""
|
|
399
|
+
Get the actual PyPI package name from an import module name.
|
|
400
|
+
|
|
401
|
+
This handles cases where the import name differs from the package name
|
|
402
|
+
(e.g., 'import fitz' from 'pymupdf' package).
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
module_name: The module name from the import statement
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
The actual package name, or None if not found
|
|
409
|
+
"""
|
|
410
|
+
root_module = module_name.split(".")[0]
|
|
411
|
+
try:
|
|
412
|
+
# Try Python 3.10+ first (has packages_distributions)
|
|
413
|
+
import importlib.metadata as importlib_metadata
|
|
414
|
+
|
|
415
|
+
# Use packages_distributions() if available (Python 3.10+)
|
|
416
|
+
if hasattr(importlib_metadata, "packages_distributions"):
|
|
417
|
+
packages_map = importlib_metadata.packages_distributions()
|
|
418
|
+
# packages_map is a dict mapping module names to list of distribution names
|
|
419
|
+
if root_module in packages_map:
|
|
420
|
+
# Return the first distribution name (usually there's only one)
|
|
421
|
+
dist_names = packages_map[root_module]
|
|
422
|
+
if dist_names:
|
|
423
|
+
return dist_names[0]
|
|
424
|
+
|
|
425
|
+
# Fallback: search all distributions
|
|
426
|
+
for dist in importlib_metadata.distributions():
|
|
427
|
+
try:
|
|
428
|
+
# Check if this distribution provides the module
|
|
429
|
+
# by looking at its files
|
|
430
|
+
files = dist.files or []
|
|
431
|
+
for file in files:
|
|
432
|
+
file_str = str(file)
|
|
433
|
+
# Check if file is the module itself or in a package directory
|
|
434
|
+
if (
|
|
435
|
+
file.suffix == ".py"
|
|
436
|
+
and (file.stem == root_module or file.stem == "__init__")
|
|
437
|
+
) or (
|
|
438
|
+
"/" in file_str
|
|
439
|
+
and (
|
|
440
|
+
file_str.startswith(f"{root_module}/")
|
|
441
|
+
or file_str.startswith(f"{root_module.replace('_', '-')}/")
|
|
442
|
+
)
|
|
443
|
+
):
|
|
444
|
+
return dist.metadata["Name"]
|
|
445
|
+
except Exception:
|
|
446
|
+
continue
|
|
447
|
+
|
|
448
|
+
except ImportError:
|
|
449
|
+
try:
|
|
450
|
+
# Fallback for older Python versions
|
|
451
|
+
import importlib_metadata
|
|
452
|
+
|
|
453
|
+
# Search all distributions
|
|
454
|
+
for dist in importlib_metadata.distributions():
|
|
455
|
+
try:
|
|
456
|
+
files = dist.files or []
|
|
457
|
+
for file in files:
|
|
458
|
+
file_str = str(file)
|
|
459
|
+
if (
|
|
460
|
+
file.suffix == ".py"
|
|
461
|
+
and (file.stem == root_module or file.stem == "__init__")
|
|
462
|
+
) or (
|
|
463
|
+
"/" in file_str
|
|
464
|
+
and (
|
|
465
|
+
file_str.startswith(f"{root_module}/")
|
|
466
|
+
or file_str.startswith(f"{root_module.replace('_', '-')}/")
|
|
467
|
+
)
|
|
468
|
+
):
|
|
469
|
+
return dist.metadata["Name"]
|
|
470
|
+
except Exception:
|
|
471
|
+
continue
|
|
472
|
+
except ImportError:
|
|
473
|
+
pass
|
|
474
|
+
except Exception:
|
|
475
|
+
pass
|
|
476
|
+
|
|
477
|
+
return None
|
|
478
|
+
|
|
397
479
|
def _extract_third_party_dependencies(
|
|
398
480
|
self, python_files: list[Path], analyzer: ImportAnalyzer
|
|
399
481
|
) -> list[str]:
|
|
@@ -401,14 +483,15 @@ class BuildManager:
|
|
|
401
483
|
Extract third-party package dependencies from Python files.
|
|
402
484
|
|
|
403
485
|
Analyzes all Python files to find imports classified as "third_party"
|
|
404
|
-
and returns a list of unique package names.
|
|
486
|
+
and returns a list of unique package names. Handles cases where the
|
|
487
|
+
import name differs from the package name (e.g., 'fitz' -> 'pymupdf').
|
|
405
488
|
|
|
406
489
|
Args:
|
|
407
490
|
python_files: List of Python file paths to analyze
|
|
408
491
|
analyzer: ImportAnalyzer instance to use for classification
|
|
409
492
|
|
|
410
493
|
Returns:
|
|
411
|
-
List of unique third-party package names (e.g., ["pypdf", "requests"])
|
|
494
|
+
List of unique third-party package names (e.g., ["pypdf", "requests", "pymupdf"])
|
|
412
495
|
"""
|
|
413
496
|
third_party_packages: set[str] = set()
|
|
414
497
|
|
|
@@ -425,17 +508,28 @@ class BuildManager:
|
|
|
425
508
|
if root_module in stdlib_modules:
|
|
426
509
|
continue
|
|
427
510
|
|
|
428
|
-
# If classified as third_party,
|
|
511
|
+
# If classified as third_party, try to get actual package name
|
|
429
512
|
if imp.classification == "third_party":
|
|
430
|
-
|
|
513
|
+
# Try to get the actual package name from metadata
|
|
514
|
+
actual_package = self._get_package_name_from_import(imp.module_name)
|
|
515
|
+
if actual_package:
|
|
516
|
+
third_party_packages.add(actual_package)
|
|
517
|
+
else:
|
|
518
|
+
# Fallback to using the import name
|
|
519
|
+
third_party_packages.add(root_module)
|
|
431
520
|
# If it's ambiguous or unresolved, and not stdlib/local/external,
|
|
432
521
|
# it's likely a third-party package that needs to be declared
|
|
433
522
|
elif imp.classification == "ambiguous" or imp.classification is None:
|
|
434
523
|
# Check if it's not a local or external module
|
|
435
524
|
if not imp.resolved_path:
|
|
436
|
-
#
|
|
437
|
-
#
|
|
438
|
-
|
|
525
|
+
# Try to get the actual package name from metadata
|
|
526
|
+
# (might work if package is installed but module path is unusual)
|
|
527
|
+
actual_package = self._get_package_name_from_import(imp.module_name)
|
|
528
|
+
if actual_package:
|
|
529
|
+
third_party_packages.add(actual_package)
|
|
530
|
+
else:
|
|
531
|
+
# Fallback: use import name (will be normalized later)
|
|
532
|
+
third_party_packages.add(root_module)
|
|
439
533
|
|
|
440
534
|
return sorted(list(third_party_packages))
|
|
441
535
|
|
|
@@ -590,17 +590,36 @@ class SubfolderBuildConfig:
|
|
|
590
590
|
updated_content = self._add_dependencies_to_pyproject(content, dependencies)
|
|
591
591
|
self.temp_pyproject.write_text(updated_content, encoding="utf-8")
|
|
592
592
|
|
|
593
|
+
def _normalize_package_name(self, package_name: str) -> str:
|
|
594
|
+
"""
|
|
595
|
+
Normalize package name for PyPI.
|
|
596
|
+
|
|
597
|
+
Converts underscores to hyphens, as PyPI package names typically use hyphens
|
|
598
|
+
while Python import names use underscores (e.g., 'better_enum' -> 'better-enum').
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
package_name: Package name from import statement
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
Normalized package name for PyPI
|
|
605
|
+
"""
|
|
606
|
+
# Convert underscores to hyphens for PyPI package names
|
|
607
|
+
# This handles the common case where import names use underscores
|
|
608
|
+
# but PyPI package names use hyphens
|
|
609
|
+
return package_name.replace("_", "-")
|
|
610
|
+
|
|
593
611
|
def _add_dependencies_to_pyproject(self, content: str, dependencies: list[str]) -> str:
|
|
594
612
|
"""
|
|
595
613
|
Add dependencies to pyproject.toml content.
|
|
596
614
|
|
|
597
615
|
Adds the specified dependencies to the [project] section's dependencies list.
|
|
598
616
|
If dependencies already exist, merges them. If no dependencies section exists,
|
|
599
|
-
creates one.
|
|
617
|
+
creates one. Package names are normalized (underscores -> hyphens) to match
|
|
618
|
+
PyPI naming conventions.
|
|
600
619
|
|
|
601
620
|
Args:
|
|
602
621
|
content: Current pyproject.toml content
|
|
603
|
-
dependencies: List of dependency names to add
|
|
622
|
+
dependencies: List of dependency names to add (will be normalized)
|
|
604
623
|
|
|
605
624
|
Returns:
|
|
606
625
|
Updated pyproject.toml content with dependencies added
|
|
@@ -608,6 +627,9 @@ class SubfolderBuildConfig:
|
|
|
608
627
|
if not dependencies:
|
|
609
628
|
return content
|
|
610
629
|
|
|
630
|
+
# Normalize package names (convert underscores to hyphens for PyPI)
|
|
631
|
+
normalized_deps = [self._normalize_package_name(dep) for dep in dependencies]
|
|
632
|
+
|
|
611
633
|
lines = content.split("\n")
|
|
612
634
|
result = []
|
|
613
635
|
in_project = False
|
|
@@ -631,8 +653,8 @@ class SubfolderBuildConfig:
|
|
|
631
653
|
if line.strip().endswith("]"):
|
|
632
654
|
in_dependencies = False
|
|
633
655
|
|
|
634
|
-
# Merge with new dependencies
|
|
635
|
-
all_deps = sorted(existing_deps | set(
|
|
656
|
+
# Merge with new dependencies (normalized)
|
|
657
|
+
all_deps = sorted(existing_deps | set(normalized_deps))
|
|
636
658
|
|
|
637
659
|
# Second pass: build result with dependencies
|
|
638
660
|
in_project = False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
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>
|
|
@@ -2,16 +2,16 @@ python_package_folder/__init__.py,sha256=DQt-uldOEKfh0MUqCvKdeNKOnpuOvpb7blYvXMy
|
|
|
2
2
|
python_package_folder/__main__.py,sha256=a-__-VLhYw-J7S7CsHdhtEvQr3RiAZxiYDvKhKTgMX4,291
|
|
3
3
|
python_package_folder/analyzer.py,sha256=w7hc2oyOoPK7tvlwcJDXnB3eiJsuGZc4BkOpTfZP7Vo,12257
|
|
4
4
|
python_package_folder/finder.py,sha256=_LvJ9xBVKv41UK5sbwbNyKmuYjAOqUbzvZhK7NCYQF8,9130
|
|
5
|
-
python_package_folder/manager.py,sha256=
|
|
5
|
+
python_package_folder/manager.py,sha256=C3atdBMZqZTFnDMuKSTtb7q2gL90AHo7Wu7u3l8nLLI,38085
|
|
6
6
|
python_package_folder/publisher.py,sha256=TSjdOvxvnWLbJCnduTK_xZBRfvsrq9kpEH-sfebeWkU,13507
|
|
7
7
|
python_package_folder/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
python_package_folder/python_package_folder.py,sha256=RPsqRcIy_LjzzTHdp4qdtFJ4-4xhtR_0YLIC0RlUxFo,8841
|
|
9
|
-
python_package_folder/subfolder_build.py,sha256=
|
|
9
|
+
python_package_folder/subfolder_build.py,sha256=LMiJ4Ck6PW1x3hmiSK-9fDH4Q6RrHqot4drx4lise3E,35310
|
|
10
10
|
python_package_folder/types.py,sha256=3yeSRR5p_3PDKEAaehW_RJ7NwJHexOIeA08bGaT1iSY,2368
|
|
11
11
|
python_package_folder/utils.py,sha256=lIkWsFKeAYAJ9TDUM99T4pUBHJVbUvCdUgkWQN-LUho,3111
|
|
12
12
|
python_package_folder/version.py,sha256=kIDP6S9trEfs9gj7lBYGxrWm4RPssRla24UtlO9Jkh4,9111
|
|
13
|
-
python_package_folder-2.0.
|
|
14
|
-
python_package_folder-2.0.
|
|
15
|
-
python_package_folder-2.0.
|
|
16
|
-
python_package_folder-2.0.
|
|
17
|
-
python_package_folder-2.0.
|
|
13
|
+
python_package_folder-2.0.4.dist-info/METADATA,sha256=4PIl6MNJWiM4Laz4Rs36zLr5O9T34BnqN9RjWDmR_VQ,33282
|
|
14
|
+
python_package_folder-2.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
python_package_folder-2.0.4.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
|
|
16
|
+
python_package_folder-2.0.4.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
|
|
17
|
+
python_package_folder-2.0.4.dist-info/RECORD,,
|
|
File without changes
|
{python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_package_folder-2.0.2.dist-info → python_package_folder-2.0.4.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|