{"slug": "faq-which-binary-did-python-hit", "title": "FAQ: Which Binary Did python Hit?", "summary": "A developer published a practical FAQ arguing that coding agents and chat summaries routinely misreport which Python binary actually executed, because command names like `python` and `python3` are shell aliases rather than the paths that run. The writeup walks through reproducible checks — `type`, `command -v`, `readlink -f`, `sys.executable`, and `python3 -m pip --version` — to prove that venv activation, shebang lines, and bare `pip` calls frequently resolve to a different interpreter than the one the agent claims to have used. The piece was prepared as part of MonkeyCode's product outreach and was drafted using the company's free model access.", "body_md": "Ever trust a chat that said tests passed?\n\nThen watch the import fail on the box?\n\nThe chat summary still looks clean and final.\n\nThe binary on disk is a different story.\n\nThis FAQ is not about clever prompts.\n\nIt is about which file actually ran.\n\nA free model will type `python` with confidence.\n\nA free server will resolve that name somehow.\n\nThose two steps are not one contract.\n\nWhy do we keep treating them as one?\n\nCoding agents talk in command strings.\n\nUnix shells talk in inodes and argv.\n\nDo you store the inode? Or the story?\n\nI want a check you can finish in minutes.\n\nNo dashboards. No vibes. No screenshots.\n\nPrint paths. Print exit codes. Then decide.\n\nI draft those checks with MonkeyCode free model access.\n\nI run the same checks on the free server option.\n\nDisclosure: This article was prepared as part of MonkeyCode's product outreach.\n\nThe product does not freeze your `PATH`.\n\nThe box does not narrate `sys.executable`.\n\nYou still have to print both. Every time.\n\nHold three facts. Repeat them out loud.\n\n`python` is not a binary.\nIf those feel obvious, good. Keep reading.\n\nThe myths below still sneak into agent logs.\n\n`python3`\nDevelopers repeat this on every new image.\n\n\"The box is modern. `python` means three.\"\n\nDoes it? Did you type `type python` yet?\n\nOn some images, `python` is missing entirely.\n\nOn others it is a leftover symlink you skipped.\n\nOn others it is not the `python3` you just used.\n\nEvidence is a command. Not a feeling.\n\n```\ntype python\ntype python3\ncommand -v python python3\nreadlink -f \"$(command -v python3)\"\npython3 -c \"import sys; print(sys.version.split()[0]); print(sys.executable)\"\n```\n\nCorrected model: names are aliases.\n\nBinaries are paths. Only paths execute.\n\nIf `command -v python` prints nothing, stop.\n\nThe agent's `python` line already failed.\n\nThe chat may still say the step is done.\n\nWhy would you believe that sentence?\n\nYou saw `python3 -m venv .venv`.\n\nYou saw a line that said activated.\n\nDid the next tool call share that shell?\n\nMany agent runners spawn a fresh shell.\n\nActivation is a shell function plus `PATH`.\n\nIt dies when that shell process exits.\n\nSo the venv exists as a directory.\n\nYour next `python3` may still be system.\n\nThat is not a model bug. That is Unix.\n\n```\npython3 -m venv .venv\n# new shell, no source on purpose\ncommand -v python3\nprintf 'VIRTUAL_ENV=%s\\n' \"${VIRTUAL_ENV-}\"\n.venv/bin/python -c \"import sys; print(sys.executable)\"\n```\n\nCorrected model: a venv is a folder.\n\nActivation is optional sugar for humans.\n\nAbsolute `.venv/bin/python` is the contract.\n\nWould you pin a CI job to bare `python`?\n\nI would not. Neither should the agent.\n\nThe file starts with `#!/usr/bin/env python`.\n\nLooks neat. Looks intentional. Looks false.\n\nWho invoked the file, and with which argv?\n\n`python3 app.py` ignores the shebang.\n\n`bash app.py` ignores the shebang too.\n\n`./app.py` uses it, if the mode allows exec.\n\n``` python\nhead -n 1 app.py\nls -l app.py\ncommand -v env\n/usr/bin/env python -c \"import sys; print(sys.executable)\" || true\n./app.py || echo \"direct exec failed\"\n```\n\nIf the file is not executable, pause.\n\n`./app.py` never ran on that box.\n\nMaybe the agent used `python3 app.py`.\n\nThen the shebang never mattered at all.\n\nCorrected model: shebang is a kernel feature.\n\nIt applies to `execve` of that file path.\n\nIt is not a hint the interpreter rereads.\n\n`pip install` hits the interpreter you used\nThis one burns hours and lockfiles.\n\n`python3` was one prefix. `pip` was another.\n\nInstall succeeded. Import failed. Chat shrugged.\n\nIt is not a mystery. It is two binaries.\n\nDid `pip --version` mention the same path?\n\n```\ncommand -v pip pip3 python3\npython3 -m pip --version\npip --version || echo \"bare pip missing\"\npython3 -c \"import sys; print('\\n'.join(sys.path[:4]))\"\n```\n\nAlways `python3 -m pip` on a throwaway box.\n\nOr `.venv/bin/python -m pip`. Never bare `pip`.\n\nBare `pip` is how silent no-ops get shipped.\n\nCorrected model: pip belongs to one interpreter.\n\nInstalling with the wrong pip is a miss.\n\nThe right interpreter never saw the wheel.\n\nThe model said all tests passed.\n\nDid pytest print a real exit code?\n\nDid you capture `$?`, or only the prose?\n\nChat UIs compress stdout. They hide stderr.\n\nThey paraphrase tracebacks into calm English.\n\nThey do not store `sys.executable` for you.\n\n``` python\npython3 -c \"import sys; print(sys.executable)\"\n.venv/bin/python -m pytest -q\necho \"exit:$?\"\n```\n\nCorrected model: success is an exit code.\n\nSuccess also names the interpreter path.\n\nA sentence from a model is a rumor.\n\nWould you merge on a rumor in code review?\n\nThen do not merge on a chat recap either.\n\nDo not argue with the chat window.\n\nWrite a file. Run the file. Keep the file.\n\nThis script is a labeled example. Run it locally on the target box.\n\n``` bash\n#!/usr/bin/env bash\n# interp_audit.sh — labeled example, run on the target box\nset -u\necho \"=== shell ===\"\nprintf 'pid=%s user=%s pwd=%s\\n' \"$$\" \"$(id -un)\" \"$PWD\"\necho \"=== PATH head ===\"\nprintf '%s\\n' \"$PATH\" | tr ':' '\\n' | head -n 8\necho \"=== names ===\"\nfor n in python python3 pip pip3 pytest; do\n  if command -v \"$n\" >/dev/null 2>&1; then\n    printf '%-8s -> %s\\n' \"$n\" \"$(command -v \"$n\")\"\n  else\n    printf '%-8s -> MISSING\\n' \"$n\"\n  fi\ndone\necho \"=== python3 details ===\"\nif command -v python3 >/dev/null 2>&1; then\n  python3 - <<'PY'\nimport sys, os\nprint(\"executable\", sys.executable)\nprint(\"prefix    \", sys.prefix)\nprint(\"base      \", getattr(sys, \"base_prefix\", \"\"))\nprint(\"version   \", sys.version.split()[0])\nprint(\"venv?     \", sys.prefix != getattr(sys, \"base_prefix\", sys.prefix))\nprint(\"VIRTUAL_ENV\", os.environ.get(\"VIRTUAL_ENV\", \"\"))\nPY\n  python3 -m pip --version || echo \"python3 -m pip failed\"\nfi\necho \"=== venv dir ===\"\nif [ -x .venv/bin/python ]; then\n  .venv/bin/python -c \"import sys; print('venv_exec', sys.executable)\"\n  .venv/bin/python -m pip --version || true\nelse\n  echo \"no .venv/bin/python\"\nfi\necho \"=== reminder ===\"\necho \"print exit:\\$? after every real test command\"\n```\n\nSave it next to the project.\n\n`chmod +x interp_audit.sh`.\n\nRun it in the same cwd the agent used.\n\nThen fill the table. The table is the artifact.\n\nThe chat is not the artifact. Never was.\n\n| You saw in chat | Likely myth | Run this | Trust this instead | \n|---|---|---|---|\n| \"python is available\" | Myth 1 | `type python; type python3` | The printed path | \n| \"venv activated\" | Myth 2 | `printf '%s\\n' \"${VIRTUAL_ENV-}\"` | `.venv/bin/python` | \n| \"script is executable\" | Myth 3 | `head -n 1 app.py; ls -l app.py` | argv plus mode bits | \n| \"package installed\" | Myth 4 | `python3 -m pip show NAME` | that command's output | \n| \"tests passed\" | Myth 5 | `echo $?` and`sys.executable` | exit code and path | \n\nCompare the left column with the right.\n\nThe left column is theater from a recap.\n\nThe right column is what the box did.\n\nWant a wrong-pip failure to show up fast?\n\nUse this labeled, unexecuted example.\n\n``` python\n# canary_interp.py — unexecuted example\nimport sys\nprint(\"CANARY\", sys.executable)\ntry:\n    import requests  # swap for the package you expected\n    print(\"import_ok\", getattr(requests, \"__file__\", \"?\"))\nexcept ImportError as exc:\n    print(\"import_fail\", exc)\n    raise SystemExit(2)\n```\n\nRun it two ways. Read both lines.\n\n```\npython3 canary_interp.py; echo exit:$?\n.venv/bin/python canary_interp.py; echo exit:$?\n```\n\nDo those two `CANARY` paths match?\n\nIf not, which path did the agent mean?\n\nIf you cannot answer, you cannot ship.\n\n`sys.executable` for the test command?`python3 -m pip --version` right now?`echo $?` print after pytest?\nIf any answer is \"the chat said so,\" stop.\n\nYou are debugging prose. Not a process.\n\nThis audit does not pin a lockfile.\n\nIt does not replace `uv` or Poetry flows.\n\nIt does not freeze a base image either.\n\nShims from pyenv resolve later than you think.\n\nPrint `sys.executable` anyway. Do not guess.\n\nThe script assumes a Unix shell and `head`.\n\nWindows needs `where` and `py -0p`. Skip this file there.\n\nFree servers get recycled without warning.\n\nYour audit is a snapshot of one shell.\n\nRerun it after every fresh session.\n\nWho should skip this whole approach?\n\n`.venv/bin/python` everywhere.\nA free model will still invent a `python` name.\n\nA free server will still pick a default binary.\n\nThe audit is a bridge. It is not a guarantee.\n\nI trust three lines. Only three.\n\nEverything else is a story about commands.\n\nStories are cheap. Paths are not cheap.\n\nAsk the chat for a recap if you want color.\n\nThen ignore the recap. Open the audit file.\n\nIf those two paths disagree, the myth already won.", "url": "https://wpnews.pro/news/faq-which-binary-did-python-hit", "canonical_source": "https://dev.to/gitlab_3188/faq-which-binary-did-python-hit-2d0", "published_at": "2026-09-13 16:09:55+00:00", "updated_at": "2026-09-13 16:14:24.978116+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["MonkeyCode", "Python", "pip", "Unix"], "alternates": {"html": "https://wpnews.pro/news/faq-which-binary-did-python-hit", "markdown": "https://wpnews.pro/news/faq-which-binary-did-python-hit.md", "text": "https://wpnews.pro/news/faq-which-binary-did-python-hit.txt", "jsonld": "https://wpnews.pro/news/faq-which-binary-did-python-hit.jsonld"}}