pipsize.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import os
  2. import importlib.metadata
  3. import importlib.util
  4. import json
  5. import sys
  6. def calc_container(path):
  7. """Calculate total size of a directory or file."""
  8. if os.path.isfile(path):
  9. try:
  10. return os.path.getsize(path)
  11. except (OSError, FileNotFoundError):
  12. return 0
  13. total_size = 0
  14. for dirpath, dirnames, filenames in os.walk(path):
  15. for f in filenames:
  16. fp = os.path.join(dirpath, f)
  17. try:
  18. total_size += os.path.getsize(fp)
  19. except (OSError, FileNotFoundError):
  20. continue
  21. return total_size
  22. def get_package_location(package_name):
  23. """Get the actual location of a package's files."""
  24. try:
  25. spec = importlib.util.find_spec(package_name)
  26. if spec is None:
  27. return None
  28. if spec.submodule_search_locations:
  29. # Return the first location for namespace packages
  30. return spec.submodule_search_locations[0]
  31. elif spec.origin:
  32. # For single-file modules, return the file path itself
  33. return spec.origin
  34. except ImportError:
  35. return None
  36. def get_package_sizes(min_size_mb=0.1):
  37. """Get sizes of installed packages above minimum size threshold."""
  38. package_sizes = []
  39. # Get all installed distributions
  40. for dist in importlib.metadata.distributions():
  41. try:
  42. package_name = dist.metadata["Name"]
  43. location = get_package_location(package_name.replace("-", "_"))
  44. if location and os.path.exists(location):
  45. size = calc_container(location)
  46. size_mb = size / (1024 * 1024)
  47. if size_mb > min_size_mb:
  48. package_sizes.append((package_name, size))
  49. except Exception as e:
  50. print(
  51. f"Error processing {dist.metadata.get('Name', 'Unknown package')}: {e}"
  52. )
  53. return package_sizes
  54. def main():
  55. # Get and sort package sizes
  56. package_sizes = get_package_sizes()
  57. package_sizes.sort(key=lambda x: x[1], reverse=True)
  58. # Convert sizes to MB and prepare data
  59. table_data = [(name, size/(1024*1024)) for name, size in package_sizes]
  60. total_size = sum(size for _, size in package_sizes)/(1024*1024)
  61. # Check if --json flag is present
  62. if "--json" in sys.argv:
  63. try:
  64. output_file = sys.argv[sys.argv.index("--json") + 1]
  65. json_data = {
  66. "packages": [{
  67. "name": name,
  68. "size_mb": round(size, 2)
  69. } for name, size in table_data],
  70. "total_size_mb": round(total_size, 2)
  71. }
  72. with open(output_file, 'w') as f:
  73. json.dump(json_data, f, indent=2)
  74. print(f"JSON data written to {output_file}")
  75. return
  76. except IndexError:
  77. print("Error: Please provide a filename after --json")
  78. sys.exit(1)
  79. except Exception as e:
  80. print(f"Error writing JSON file: {e}")
  81. sys.exit(1)
  82. # Original table output code
  83. max_name_width = max(len(name) for name, _ in table_data)
  84. max_name_width = max(max_name_width, len("Package"))
  85. print(f"\n{'Package':<{max_name_width}} | Size (MB)")
  86. print("-" * max_name_width + "-+-" + "-" * 10)
  87. for name, size in table_data:
  88. print(f"{name:<{max_name_width}} | {size:>8.2f}")
  89. print(f"\nTotal size: {total_size:.2f} MB\n")
  90. if __name__ == "__main__":
  91. main()