python-package-folder 1.4.1__py3-none-any.whl → 2.0.2__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 +58 -0
- python_package_folder/subfolder_build.py +117 -0
- {python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/METADATA +1 -1
- {python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/RECORD +7 -7
- {python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/WHEEL +0 -0
- {python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/entry_points.txt +0 -0
- {python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/licenses/LICENSE +0 -0
python_package_folder/manager.py
CHANGED
|
@@ -268,6 +268,19 @@ class BuildManager:
|
|
|
268
268
|
for dep in external_deps:
|
|
269
269
|
self._copy_dependency(dep)
|
|
270
270
|
|
|
271
|
+
# For subfolder builds, extract third-party dependencies and add to pyproject.toml
|
|
272
|
+
if self._is_subfolder_build() and self.subfolder_config:
|
|
273
|
+
# Re-analyze all Python files (including copied dependencies) to find third-party imports
|
|
274
|
+
all_python_files = analyzer.find_all_python_files(self.src_dir)
|
|
275
|
+
third_party_deps = self._extract_third_party_dependencies(all_python_files, analyzer)
|
|
276
|
+
if third_party_deps:
|
|
277
|
+
print(
|
|
278
|
+
f"Found {len(third_party_deps)} third-party dependencies: {', '.join(third_party_deps)}"
|
|
279
|
+
)
|
|
280
|
+
self.subfolder_config.add_third_party_dependencies(third_party_deps)
|
|
281
|
+
else:
|
|
282
|
+
print("No third-party dependencies found in subfolder code")
|
|
283
|
+
|
|
271
284
|
# Report ambiguous imports
|
|
272
285
|
self._report_ambiguous_imports(python_files)
|
|
273
286
|
|
|
@@ -381,6 +394,51 @@ class BuildManager:
|
|
|
381
394
|
elif src_item.is_dir():
|
|
382
395
|
self._copytree_excluding(src_item, dst_item)
|
|
383
396
|
|
|
397
|
+
def _extract_third_party_dependencies(
|
|
398
|
+
self, python_files: list[Path], analyzer: ImportAnalyzer
|
|
399
|
+
) -> list[str]:
|
|
400
|
+
"""
|
|
401
|
+
Extract third-party package dependencies from Python files.
|
|
402
|
+
|
|
403
|
+
Analyzes all Python files to find imports classified as "third_party"
|
|
404
|
+
and returns a list of unique package names.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
python_files: List of Python file paths to analyze
|
|
408
|
+
analyzer: ImportAnalyzer instance to use for classification
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
List of unique third-party package names (e.g., ["pypdf", "requests"])
|
|
412
|
+
"""
|
|
413
|
+
third_party_packages: set[str] = set()
|
|
414
|
+
|
|
415
|
+
for file_path in python_files:
|
|
416
|
+
imports = analyzer.extract_imports(file_path)
|
|
417
|
+
for imp in imports:
|
|
418
|
+
analyzer.classify_import(imp, self.src_dir)
|
|
419
|
+
|
|
420
|
+
# Extract the root package name (first part of module name)
|
|
421
|
+
root_module = imp.module_name.split(".")[0]
|
|
422
|
+
|
|
423
|
+
# Skip if it's a standard library module
|
|
424
|
+
stdlib_modules = analyzer.get_stdlib_modules()
|
|
425
|
+
if root_module in stdlib_modules:
|
|
426
|
+
continue
|
|
427
|
+
|
|
428
|
+
# If classified as third_party, add it
|
|
429
|
+
if imp.classification == "third_party":
|
|
430
|
+
third_party_packages.add(root_module)
|
|
431
|
+
# If it's ambiguous or unresolved, and not stdlib/local/external,
|
|
432
|
+
# it's likely a third-party package that needs to be declared
|
|
433
|
+
elif imp.classification == "ambiguous" or imp.classification is None:
|
|
434
|
+
# Check if it's not a local or external module
|
|
435
|
+
if not imp.resolved_path:
|
|
436
|
+
# This is likely a third-party package that's not installed
|
|
437
|
+
# in the build environment but needs to be declared
|
|
438
|
+
third_party_packages.add(root_module)
|
|
439
|
+
|
|
440
|
+
return sorted(list(third_party_packages))
|
|
441
|
+
|
|
384
442
|
def _report_ambiguous_imports(self, python_files: list[Path]) -> None:
|
|
385
443
|
"""
|
|
386
444
|
Report any ambiguous imports that couldn't be resolved.
|
|
@@ -254,6 +254,9 @@ class SubfolderBuildConfig:
|
|
|
254
254
|
# If original pyproject.toml exists, temporarily move it
|
|
255
255
|
if original_pyproject.exists():
|
|
256
256
|
backup_path = self.project_root / "pyproject.toml.original"
|
|
257
|
+
# Remove backup if it already exists (from previous failed test or run)
|
|
258
|
+
if backup_path.exists():
|
|
259
|
+
backup_path.unlink()
|
|
257
260
|
original_pyproject.rename(backup_path)
|
|
258
261
|
self.original_pyproject_backup = backup_path
|
|
259
262
|
|
|
@@ -292,6 +295,9 @@ class SubfolderBuildConfig:
|
|
|
292
295
|
|
|
293
296
|
# Temporarily move original to backup location
|
|
294
297
|
backup_path = self.project_root / "pyproject.toml.original"
|
|
298
|
+
# Remove backup if it already exists (from previous failed test or run)
|
|
299
|
+
if backup_path.exists():
|
|
300
|
+
backup_path.unlink()
|
|
295
301
|
original_pyproject.rename(backup_path)
|
|
296
302
|
self.original_pyproject_backup = backup_path
|
|
297
303
|
|
|
@@ -559,6 +565,117 @@ class SubfolderBuildConfig:
|
|
|
559
565
|
|
|
560
566
|
return "\n".join(result)
|
|
561
567
|
|
|
568
|
+
def add_third_party_dependencies(self, dependencies: list[str]) -> None:
|
|
569
|
+
"""
|
|
570
|
+
Add third-party dependencies to the temporary pyproject.toml.
|
|
571
|
+
|
|
572
|
+
This method updates the pyproject.toml file that was created for the subfolder
|
|
573
|
+
build by adding the specified dependencies to the [project.dependencies] section.
|
|
574
|
+
|
|
575
|
+
Args:
|
|
576
|
+
dependencies: List of third-party package names to add (e.g., ["pypdf", "requests"])
|
|
577
|
+
"""
|
|
578
|
+
if not self.temp_pyproject or not self.temp_pyproject.exists():
|
|
579
|
+
print(
|
|
580
|
+
f"Warning: Cannot add third-party dependencies - pyproject.toml not found at {self.temp_pyproject}",
|
|
581
|
+
file=sys.stderr,
|
|
582
|
+
)
|
|
583
|
+
return
|
|
584
|
+
|
|
585
|
+
if not dependencies:
|
|
586
|
+
return
|
|
587
|
+
|
|
588
|
+
print(f"Adding third-party dependencies to pyproject.toml: {', '.join(dependencies)}")
|
|
589
|
+
content = self.temp_pyproject.read_text(encoding="utf-8")
|
|
590
|
+
updated_content = self._add_dependencies_to_pyproject(content, dependencies)
|
|
591
|
+
self.temp_pyproject.write_text(updated_content, encoding="utf-8")
|
|
592
|
+
|
|
593
|
+
def _add_dependencies_to_pyproject(self, content: str, dependencies: list[str]) -> str:
|
|
594
|
+
"""
|
|
595
|
+
Add dependencies to pyproject.toml content.
|
|
596
|
+
|
|
597
|
+
Adds the specified dependencies to the [project] section's dependencies list.
|
|
598
|
+
If dependencies already exist, merges them. If no dependencies section exists,
|
|
599
|
+
creates one.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
content: Current pyproject.toml content
|
|
603
|
+
dependencies: List of dependency names to add
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
Updated pyproject.toml content with dependencies added
|
|
607
|
+
"""
|
|
608
|
+
if not dependencies:
|
|
609
|
+
return content
|
|
610
|
+
|
|
611
|
+
lines = content.split("\n")
|
|
612
|
+
result = []
|
|
613
|
+
in_project = False
|
|
614
|
+
in_dependencies = False
|
|
615
|
+
dependencies_added = False
|
|
616
|
+
existing_deps: set[str] = set()
|
|
617
|
+
|
|
618
|
+
# First pass: find existing dependencies
|
|
619
|
+
for line in lines:
|
|
620
|
+
if line.strip().startswith("[project]"):
|
|
621
|
+
in_project = True
|
|
622
|
+
elif line.strip().startswith("[") and in_project:
|
|
623
|
+
in_project = False
|
|
624
|
+
elif in_project and re.match(r"^\s*dependencies\s*=\s*\[", line):
|
|
625
|
+
in_dependencies = True
|
|
626
|
+
elif in_dependencies:
|
|
627
|
+
# Extract existing dependency names
|
|
628
|
+
dep_match = re.search(r'["\']([^"\']+)["\']', line)
|
|
629
|
+
if dep_match:
|
|
630
|
+
existing_deps.add(dep_match.group(1))
|
|
631
|
+
if line.strip().endswith("]"):
|
|
632
|
+
in_dependencies = False
|
|
633
|
+
|
|
634
|
+
# Merge with new dependencies
|
|
635
|
+
all_deps = sorted(existing_deps | set(dependencies))
|
|
636
|
+
|
|
637
|
+
# Second pass: build result with dependencies
|
|
638
|
+
in_project = False
|
|
639
|
+
in_dependencies = False
|
|
640
|
+
for line in lines:
|
|
641
|
+
if line.strip().startswith("[project]"):
|
|
642
|
+
in_project = True
|
|
643
|
+
result.append(line)
|
|
644
|
+
elif line.strip().startswith("[") and in_project:
|
|
645
|
+
# End of [project] section, add dependencies if not already present
|
|
646
|
+
if not dependencies_added:
|
|
647
|
+
result.append("dependencies = [")
|
|
648
|
+
for dep in all_deps:
|
|
649
|
+
result.append(f' "{dep}",')
|
|
650
|
+
result.append("]")
|
|
651
|
+
result.append("")
|
|
652
|
+
in_project = False
|
|
653
|
+
result.append(line)
|
|
654
|
+
elif in_project and re.match(r"^\s*dependencies\s*=\s*\[", line):
|
|
655
|
+
# Replace existing dependencies section
|
|
656
|
+
result.append("dependencies = [")
|
|
657
|
+
for dep in all_deps:
|
|
658
|
+
result.append(f' "{dep}",')
|
|
659
|
+
result.append("]")
|
|
660
|
+
dependencies_added = True
|
|
661
|
+
in_dependencies = True
|
|
662
|
+
elif in_dependencies:
|
|
663
|
+
# Skip lines in existing dependencies section (already replaced)
|
|
664
|
+
if line.strip().endswith("]"):
|
|
665
|
+
in_dependencies = False
|
|
666
|
+
else:
|
|
667
|
+
result.append(line)
|
|
668
|
+
|
|
669
|
+
# If [project] section exists but no dependencies were added, add them
|
|
670
|
+
if in_project and not dependencies_added:
|
|
671
|
+
result.append("dependencies = [")
|
|
672
|
+
for dep in all_deps:
|
|
673
|
+
result.append(f' "{dep}",')
|
|
674
|
+
result.append("]")
|
|
675
|
+
result.append("")
|
|
676
|
+
|
|
677
|
+
return "\n".join(result)
|
|
678
|
+
|
|
562
679
|
def _handle_readme(self) -> None:
|
|
563
680
|
"""
|
|
564
681
|
Handle README file for subfolder builds.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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>
|
|
@@ -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=t3mbTnn42QCLWSOC5P_XRJPi1bL9hyYaCZVdITENHXk,33764
|
|
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=IfnXW5Xx8-WMuBc8hTdbCImDIuUAiHvVe1k5V2FbwoY,34339
|
|
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-
|
|
14
|
-
python_package_folder-
|
|
15
|
-
python_package_folder-
|
|
16
|
-
python_package_folder-
|
|
17
|
-
python_package_folder-
|
|
13
|
+
python_package_folder-2.0.2.dist-info/METADATA,sha256=1AFTide6hdpSNzjVFpLai2Mo-UYyfSSlA9hcx66QJBM,33282
|
|
14
|
+
python_package_folder-2.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
python_package_folder-2.0.2.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
|
|
16
|
+
python_package_folder-2.0.2.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
|
|
17
|
+
python_package_folder-2.0.2.dist-info/RECORD,,
|
|
File without changes
|
{python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_package_folder-1.4.1.dist-info → python_package_folder-2.0.2.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|