Repository: pwndbg/pwndbg

Issue:
Officially support installation in a virtualenv
- [ ] Add a `--venv` flag to `setup.sh` that creates a new venv and installs pwndbg in it
- [ ] Remove any warnings about venvs, i.e. "Pwndbg/GDB run in virtualenv with which it may not work correctly"
- [ ] Add tests
- [ ] Add docs


Discussion:
I think one bit isn't quite clear in the description: Do we want to create a venv dedicated to pwndbg, or will we reuse the user's currently active venv? If it's the former, maybe `~/.cache` would be an option for the location of our venv?
@Cnly Whatever is best. I think we rather want to create a new virtual environment and we will just have to ask the user "where to put it", suggesting a location like `$pwndbg/.venv`, `~/.venv/pwndbg` or `~/.local/pwndbg-venv`. Alternatively, iirc we were thinking some time ago about some local cache, so maybe it could be sth like `~/.pwndbg/{venv,cache}`? I am not sure.

Resolving patch:
diff --git a/gdbinit.py b/gdbinit.py
--- a/gdbinit.py
+++ b/gdbinit.py
@@ -4,11 +4,10 @@
 import os
 import sys
 import time
+from glob import glob
 from os import environ
 from os import path
 
-import pkg_resources
-
 _profiler = cProfile.Profile()
 
 _start_time = None
@@ -16,73 +15,31 @@
     _start_time = time.time()
     _profiler.enable()
 
-# Allow users to use packages from a virtualenv
-# That's not 100% supported, but they do it on their own,
-# so we will warn them if the GDB's Python is not virtualenv's Python
-virtual_env = environ.get("VIRTUAL_ENV")
-
-if virtual_env:
-    py_exe_matches = sys.executable.startswith(virtual_env)
-
-    if not py_exe_matches:
-        venv_warning = int(environ.get("PWNDBG_VENV_WARNING", 1))
-        if venv_warning:
-            print(
-                f"""WARNING: Pwndbg/GDB run in virtualenv with which it may not work correctly ***
-  Detected Python virtual environment: VIRTUAL_ENV='{virtual_env}'
-  while GDB is built with different Python binary: {sys.executable}
-  Assuming that you installed Pwndbg dependencies into the virtual environment
-  If this is not true, this may cause import errors or other issues in Pwndbg
-  If all works for you, you can suppress this warning and all further prints
-  by setting `export PWNDBG_VENV_WARNING=0` (e.g. in ~/.bashrc or ~/.zshrc etc.)"""
-            )
-            venv_warn = print
-        else:
-            venv_warn = lambda *a, **kw: None
-
-        possible_site_packages = glob.glob(
-            path.join(virtual_env, "lib", "python*", "site-packages")
-        )
-
-        if len(possible_site_packages) > 1:
-            venv_warn("*** Found multiple site packages in virtualenv:")
-            for site_pkg in possible_site_packages:
-                venv_warn(f"    - {site_pkg}")
-
-            virtualenv_site_packages = possible_site_packages[-1]
-            venv_warn(f"*** Using the last one: {virtualenv_site_packages}")
-
-        elif len(possible_site_packages) == 1:
-            virtualenv_site_packages = possible_site_packages[-1]
-            venv_warn(f"*** Using the only site packages dir found: {virtualenv_site_packages}")
+directory, file = path.split(__file__)
+directory = path.expanduser(directory)
+directory = path.abspath(directory)
 
-        else:
-            guessed_python_directory = f"python{sys.version_info.major}.{sys.version_info.minor}"
-            virtualenv_site_packages = path.join(
-                virtual_env, "lib", guessed_python_directory, "site-packages"
-            )
-            venv_warn(
-                f"***  Not found site-packages in virtualenv, using guessed site packages Python dir: {virtualenv_site_packages}"
-            )
+# Get virtualenv's site-packages path
+venv_path = os.environ.get("PWNDBG_VENV_PATH")
+if not venv_path:
+    venv_path = os.path.join(directory, ".venv")
 
-        venv_warn("  Added detected virtualenv's Python site packages to sys.path")
-        venv_warn("")
-        sys.path.append(virtualenv_site_packages)
+if not os.path.exists(venv_path):
+    print(f"Cannot find Pwndbg virtualenv directory: {venv_path}: please re-run setup.sh")
+    sys.exit(1)
 
+site_pkgs_path = glob(os.path.join(venv_path, "lib/*/site-packages"))[0]
 
-directory, file = path.split(__file__)
-directory = path.expanduser(directory)
-directory = path.abspath(directory)
+# Set virtualenv's bin path (needed for utility tools like ropper, pwntools etc)
+bin_path = os.path.join(venv_path, "bin")
+os.environ["PATH"] = bin_path + os.pathsep + os.environ.get("PATH")
 
 # Add gdb-pt-dump directory to sys.path so it can be imported
 gdbpt = path.join(directory, "gdb-pt-dump")
 sys.path.append(directory)
+sys.path.append(site_pkgs_path)
 sys.path.append(gdbpt)
 
-# Add the dir where Pwntools binaries might be into PATH
-pwntools_bin_dir = os.path.join(pkg_resources.get_distribution("pwntools").location, "bin")
-os.environ["PATH"] = os.environ.get("PATH") + os.pathsep + pwntools_bin_dir
-
 # warn if the user has different encoding than utf-8
 encoding = locale.getpreferredencoding()
 


Test patch: