177 lines
6.3 KiB
Python
177 lines
6.3 KiB
Python
"""
|
||
install_dependencies.py
|
||
========================
|
||
Installs all Python dependencies for the PST Email Extractor and
|
||
verifies that pypff (libpff) is importable.
|
||
|
||
Run once before first use:
|
||
python install_dependencies.py
|
||
|
||
Platform notes
|
||
--------------
|
||
* Linux / macOS – pypff is usually available via pip (libpff-python) or a
|
||
system package manager.
|
||
* Windows – pypff typically requires a pre-built wheel or compiling
|
||
libpff from source. See the README for details.
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import subprocess
|
||
import platform
|
||
from importlib.util import find_spec
|
||
|
||
# ── package lists ────────────────────────────────────────────────
|
||
# Packages that install cleanly via pip on all platforms
|
||
PIP_PACKAGES: list[tuple[str, str]] = [
|
||
("tqdm", "tqdm>=4.66"), # Progress bars
|
||
]
|
||
|
||
# pypff can be installed with 'libpff-python' on Linux/macOS
|
||
PYPFF_PIP_NAME = "libpff-python"
|
||
|
||
|
||
# ── helpers ──────────────────────────────────────────────────────
|
||
|
||
def run(cmd: list[str]) -> subprocess.CompletedProcess:
|
||
"""Print and run a command, returning the result without raising."""
|
||
print(f"\n>> {' '.join(cmd)}")
|
||
return subprocess.run(cmd, check=False)
|
||
|
||
|
||
def is_importable(module: str) -> bool:
|
||
"""Return True if *module* can be found in the current environment."""
|
||
return find_spec(module) is not None
|
||
|
||
|
||
# ── steps ────────────────────────────────────────────────────────
|
||
|
||
def ensure_pip() -> None:
|
||
"""Bootstrap / upgrade pip, setuptools, and wheel."""
|
||
print("\n[1/3] Ensuring pip is up-to-date …")
|
||
run([sys.executable, "-m", "ensurepip", "--upgrade"])
|
||
run([sys.executable, "-m", "pip", "install", "--upgrade",
|
||
"pip", "setuptools", "wheel"])
|
||
|
||
|
||
def install_pip_packages() -> None:
|
||
"""Install the pip-only dependencies."""
|
||
print("\n[2/3] Installing pip packages …")
|
||
specs = [spec for _, spec in PIP_PACKAGES]
|
||
result = run([sys.executable, "-m", "pip", "install", "--upgrade"] + specs)
|
||
if result.returncode != 0:
|
||
print("\n❌ ERROR: Failed to install pip packages.")
|
||
sys.exit(result.returncode)
|
||
|
||
# Verify each import
|
||
for module, spec in PIP_PACKAGES:
|
||
ok = is_importable(module)
|
||
status = "✓" if ok else "✗"
|
||
print(f" {status} {spec} ({'ok' if ok else 'MISSING'})")
|
||
|
||
|
||
def install_pypff() -> bool:
|
||
"""
|
||
Try to install pypff via pip (works on Linux/macOS).
|
||
On Windows this usually fails; we give targeted guidance instead.
|
||
"""
|
||
print("\n[3/3] Installing pypff (libpff-python) …")
|
||
|
||
if is_importable("pypff"):
|
||
print(" ✓ pypff is already importable – skipping install.")
|
||
return True
|
||
|
||
result = run([sys.executable, "-m", "pip", "install", "--upgrade", PYPFF_PIP_NAME])
|
||
|
||
if result.returncode == 0 and is_importable("pypff"):
|
||
print(" ✓ pypff installed and importable.")
|
||
return True
|
||
|
||
# ── second attempt: some distros ship it as 'pypff' directly ──
|
||
result2 = run([sys.executable, "-m", "pip", "install", "--upgrade", "pypff"])
|
||
if result2.returncode == 0 and is_importable("pypff"):
|
||
print(" ✓ pypff installed via 'pypff' package name.")
|
||
return True
|
||
|
||
print(" ✗ Could not install pypff automatically.")
|
||
return False
|
||
|
||
|
||
# ── platform guidance ─────────────────────────────────────────────
|
||
|
||
def print_pypff_guidance() -> None:
|
||
system = platform.system()
|
||
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||
|
||
print("\n" + "=" * 72)
|
||
print("MANUAL INSTALLATION REQUIRED: pypff / libpff")
|
||
print("=" * 72)
|
||
|
||
if system == "Linux":
|
||
print(
|
||
"On Debian / Ubuntu:\n"
|
||
" sudo apt-get install python3-libpff\n"
|
||
" # or\n"
|
||
" pip install libpff-python\n\n"
|
||
"On Fedora / RHEL:\n"
|
||
" sudo dnf install libpff-devel python3-libpff\n"
|
||
)
|
||
elif system == "Darwin":
|
||
print(
|
||
"On macOS (Homebrew):\n"
|
||
" brew install libpff\n"
|
||
" pip install libpff-python\n"
|
||
)
|
||
elif system == "Windows":
|
||
print(
|
||
f"On Windows (Python {py_ver}):\n"
|
||
" Option 1 – Pre-built wheel (if available for your Python version):\n"
|
||
" pip install libpff-python\n\n"
|
||
" Option 2 – Build from source:\n"
|
||
" https://github.com/libyal/libpff/blob/main/documentation/Building.md\n\n"
|
||
" Option 3 – Use WSL (Windows Subsystem for Linux) and follow the Linux steps.\n"
|
||
)
|
||
else:
|
||
print(
|
||
" Please visit https://github.com/libyal/libpff for build instructions\n"
|
||
f" appropriate for your platform ({system}).\n"
|
||
)
|
||
|
||
print("=" * 72)
|
||
|
||
|
||
# ── main ──────────────────────────────────────────────────────────
|
||
|
||
def main() -> None:
|
||
print("PST Email Extractor – Dependency Installer")
|
||
print("=" * 72)
|
||
print(f" Python: {sys.version.replace(chr(10), ' ')}")
|
||
print(f" Executable: {sys.executable}")
|
||
print(f" Platform: {platform.platform()}")
|
||
print(f" CWD: {os.getcwd()}")
|
||
|
||
ensure_pip()
|
||
install_pip_packages()
|
||
has_pypff = install_pypff()
|
||
|
||
# ── summary ──────────────────────────────────────────────────
|
||
print("\n" + "=" * 72)
|
||
print("Installation summary")
|
||
print("=" * 72)
|
||
for module, spec in PIP_PACKAGES:
|
||
ok = is_importable(module)
|
||
print(f" {'✓' if ok else '✗'} {spec}")
|
||
print(f" {'✓' if has_pypff else '✗'} pypff (libpff)")
|
||
print("=" * 72)
|
||
|
||
if not has_pypff:
|
||
print_pypff_guidance()
|
||
sys.exit(2)
|
||
|
||
print("\n✅ All dependencies are installed.")
|
||
print(" You can now run: python Extract.py [directory]")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|