Files
PST_Contents_to_TXT/install_dependencies.py
T
2026-09-13 19:53:09 +01:00

177 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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()