|
27 | 27 | - Verify package integrity |
28 | 28 | - Handle .dist-info and .egg-info metadata |
29 | 29 | - Support for namespace packages |
| 30 | +- Auto-detect and copy to venv site-packages |
30 | 31 | """ |
31 | 32 |
|
32 | 33 | import os |
@@ -503,6 +504,96 @@ def get_statistics(self) -> Dict: |
503 | 504 | "total_size_mb": round(total_size / (1024 * 1024), 2), |
504 | 505 | } |
505 | 506 |
|
| 507 | + @staticmethod |
| 508 | + def find_venv_site_packages(venv_path: Optional[str] = None) -> Optional[str]: |
| 509 | + """ |
| 510 | + Find the site-packages directory in a virtual environment. |
| 511 | +
|
| 512 | + Args: |
| 513 | + venv_path: Path to virtual environment. If None, uses current venv. |
| 514 | +
|
| 515 | + Returns: |
| 516 | + Path to site-packages directory or None. |
| 517 | + """ |
| 518 | + if venv_path is None: |
| 519 | + # Try to detect current venv |
| 520 | + venv_path = os.environ.get("VIRTUAL_ENV") |
| 521 | + if not venv_path: |
| 522 | + logger.warning("No virtual environment detected") |
| 523 | + return None |
| 524 | + |
| 525 | + # Common site-packages locations |
| 526 | + possible_paths = [ |
| 527 | + os.path.join(venv_path, "lib", "python3.9", "site-packages"), |
| 528 | + os.path.join(venv_path, "lib", "python3.10", "site-packages"), |
| 529 | + os.path.join(venv_path, "lib", "python3.11", "site-packages"), |
| 530 | + os.path.join(venv_path, "lib", "python3.12", "site-packages"), |
| 531 | + os.path.join(venv_path, "lib", "python3.13", "site-packages"), |
| 532 | + os.path.join(venv_path, "Lib", "site-packages"), # Windows |
| 533 | + ] |
| 534 | + |
| 535 | + for path in possible_paths: |
| 536 | + if os.path.isdir(path): |
| 537 | + logger.debug(f"Found venv site-packages: {path}") |
| 538 | + return path |
| 539 | + |
| 540 | + logger.warning(f"Could not find site-packages in venv: {venv_path}") |
| 541 | + return None |
| 542 | + |
| 543 | + def copy_to_venv( |
| 544 | + self, |
| 545 | + package_names: List[str], |
| 546 | + venv_path: Optional[str] = None, |
| 547 | + include_metadata: bool = True, |
| 548 | + skip_errors: bool = True, |
| 549 | + ) -> Tuple[List[str], Dict[str, str]]: |
| 550 | + """ |
| 551 | + Copy packages to virtual environment site-packages. |
| 552 | +
|
| 553 | + Args: |
| 554 | + package_names: List of package names to copy. |
| 555 | + venv_path: Path to virtual environment. If None, uses current venv. |
| 556 | + include_metadata: Whether to copy metadata. |
| 557 | + skip_errors: Whether to continue on error. |
| 558 | +
|
| 559 | + Returns: |
| 560 | + Tuple of (copied_packages, failed_packages). |
| 561 | + """ |
| 562 | + site_packages = self.find_venv_site_packages(venv_path) |
| 563 | + if not site_packages: |
| 564 | + error_msg = "Could not find venv site-packages directory" |
| 565 | + logger.error(error_msg) |
| 566 | + return [], {name: error_msg for name in package_names} |
| 567 | + |
| 568 | + logger.info(f"Copying packages to venv: {site_packages}") |
| 569 | + return self.copy_packages(package_names, site_packages, include_metadata, skip_errors) |
| 570 | + |
| 571 | + def copy_to_venv_with_dependencies( |
| 572 | + self, |
| 573 | + package_names: List[str], |
| 574 | + venv_path: Optional[str] = None, |
| 575 | + include_metadata: bool = True, |
| 576 | + ) -> Tuple[List[str], Dict[str, str]]: |
| 577 | + """ |
| 578 | + Copy packages and their dependencies to virtual environment. |
| 579 | +
|
| 580 | + Args: |
| 581 | + package_names: List of package names to copy. |
| 582 | + venv_path: Path to virtual environment. If None, uses current venv. |
| 583 | + include_metadata: Whether to copy metadata. |
| 584 | +
|
| 585 | + Returns: |
| 586 | + Tuple of (copied_packages, failed_packages). |
| 587 | + """ |
| 588 | + site_packages = self.find_venv_site_packages(venv_path) |
| 589 | + if not site_packages: |
| 590 | + error_msg = "Could not find venv site-packages directory" |
| 591 | + logger.error(error_msg) |
| 592 | + return [], {name: error_msg for name in package_names} |
| 593 | + |
| 594 | + logger.info(f"Copying packages with dependencies to venv: {site_packages}") |
| 595 | + return self.copy_packages_with_dependencies(package_names, site_packages, include_metadata) |
| 596 | + |
506 | 597 |
|
507 | 598 | def copy_package_simple( |
508 | 599 | package_name: str, destination: str, source_dir: Optional[str] = None |
@@ -566,6 +657,54 @@ def list_system_packages(source_dir: Optional[str] = None) -> List[str]: |
566 | 657 | return [] |
567 | 658 |
|
568 | 659 |
|
| 660 | +def copy_to_venv_simple( |
| 661 | + package_names: List[str], |
| 662 | + venv_path: Optional[str] = None, |
| 663 | + source_dir: Optional[str] = None, |
| 664 | +) -> Tuple[List[str], Dict[str, str]]: |
| 665 | + """ |
| 666 | + Simple function to copy packages to virtual environment. |
| 667 | +
|
| 668 | + Args: |
| 669 | + package_names: List of package names to copy. |
| 670 | + venv_path: Path to virtual environment. If None, uses current venv. |
| 671 | + source_dir: Source directory (optional). |
| 672 | +
|
| 673 | + Returns: |
| 674 | + Tuple of (copied_packages, failed_packages). |
| 675 | + """ |
| 676 | + try: |
| 677 | + copier = PackageCopier(source_dir=source_dir) |
| 678 | + return copier.copy_to_venv(package_names, venv_path) |
| 679 | + except Exception as e: |
| 680 | + logger.error(f"Error copying to venv: {e}") |
| 681 | + return [], {name: str(e) for name in package_names} |
| 682 | + |
| 683 | + |
| 684 | +def copy_to_venv_with_deps_simple( |
| 685 | + package_names: List[str], |
| 686 | + venv_path: Optional[str] = None, |
| 687 | + source_dir: Optional[str] = None, |
| 688 | +) -> Tuple[List[str], Dict[str, str]]: |
| 689 | + """ |
| 690 | + Simple function to copy packages with dependencies to virtual environment. |
| 691 | +
|
| 692 | + Args: |
| 693 | + package_names: List of package names to copy. |
| 694 | + venv_path: Path to virtual environment. If None, uses current venv. |
| 695 | + source_dir: Source directory (optional). |
| 696 | +
|
| 697 | + Returns: |
| 698 | + Tuple of (copied_packages, failed_packages). |
| 699 | + """ |
| 700 | + try: |
| 701 | + copier = PackageCopier(source_dir=source_dir) |
| 702 | + return copier.copy_to_venv_with_dependencies(package_names, venv_path) |
| 703 | + except Exception as e: |
| 704 | + logger.error(f"Error copying to venv with dependencies: {e}") |
| 705 | + return [], {name: str(e) for name in package_names} |
| 706 | + |
| 707 | + |
569 | 708 | if __name__ == "__main__": |
570 | 709 | # Example usage |
571 | 710 | import argparse |
|
0 commit comments