TL;DR: the import name is saml2 but the distribution you install is pysaml2, and it has to be installed into the exact interpreter that runs your code. Install with that interpreter's own pip module (python -m pip install pysaml2), never a bare pip that may belong to another Python, then verify with the same interpreter. If the install itself fails, fix the system prerequisites first, especially the xmlsec1 binary and the build libraries cryptography needs on minimal images. If pip reports pysaml2 as installed and the import still fails, you almost always have an interpreter mismatch or a local file named saml2.py shadowing the real package.

```text
ModuleNotFoundError: No module named 'saml2'
```

Variant spellings of the same failure include ImportError: No module named saml2 on older Python, and a traceback ending in from saml2 import BINDING_HTTP_REDIRECT, from saml2.client import Saml2Client, or from saml2.config import Config.

## Steps

### 1. Install pysaml2 into the interpreter that will run the code

Do not use a bare pip command. A bare pip can belong to a system Python, a different virtualenv, or Python 2 era tooling, while your app runs under another interpreter entirely. Use the module form, which guarantees pip and the app share one interpreter.

```bash
python -m pip install --upgrade pip
python -m pip install pysaml2
python -c "import sys; print(sys.executable)"
```

Expected output: the install ends with a Successfully installed line that lists pysaml2 and its dependencies (cryptography, defusedxml, pyOpenSSL, python-dateutil, requests, xmlschema), and the last command prints the interpreter path you expect, for example the bin/python inside your project virtualenv. Success check: proceed to step 2 with the same python command, in the same shell, with the same virtualenv active.

Note on names: pip install saml2 does not give you this library, and pip install python3-saml gives you a different library whose imports start with onelogin. Only pysaml2 provides the top-level saml2 package.

### 2. Verify the import and where it resolved from

```bash
python -c "import saml2; print(saml2.__file__)"
python -c "from saml2 import BINDING_HTTP_REDIRECT; from saml2.client import Saml2Client; print('ok')"
python -m pip show pysaml2
```

Expected output: ok, a file path inside site-packages in the active environment, and pip show reporting Name: pysaml2 with a Location that matches that same environment. Success check: the directory in the saml2 file path and the Location in pip show are the same site-packages. If they differ, or the path points into your project folder, go to step 5.

### 3. When pip says it is already installed but the import still fails

This is an interpreter mismatch, not a broken package. Diagnose it by comparing the two environments directly.

```bash
which python; which pip; python -m pip --version
python -c "import sys; print(sys.executable); print(sys.path)"
pip show pysaml2
```

Expected output: pip show (the bare pip) may list pysaml2 while python -m pip show does not, or the two report different Location values. That difference is the bug: the package lives in an environment your code does not use. Common forms: you activated the virtualenv in one terminal but run the app from another; an IDE, Jupyter, gunicorn, uWSGI, cron, or a systemd service uses its own interpreter or virtualenv; or Docker installed into one stage or user and runs as another.

Fix: reinstall using step 1 with the exact interpreter the failing process uses, add pysaml2 to your requirements file or dependency manifest so fresh environments get it, and restart the server, kernel, or service so it picks up the newly installed package. For Jupyter, install into the kernel's interpreter and restart the kernel; installing into the terminal's Python does nothing for a kernel running elsewhere. Success check: python -m pip show pysaml2 and the step 2 import both succeed when run the way the app itself is launched.

### 4. When the install itself fails

A failed install leaves no saml2 module at all, and the real error is in the install log, usually well above the final line. pysaml2 is a SAML library with native-adjacent prerequisites:

- The pysaml2 documentation lists the xmlsec1 binary as a prerequisite. The Python package can import without it in simple cases, but signing and signature verification through the default xmlsec backend need the binary at runtime, and many deployment guides and framework integrations fail hard when it is missing. Install it with your OS package manager: on Debian and Ubuntu the package is called xmlsec1, on macOS via Homebrew it is libxmlsec1, and then confirm the binary is on the path with the command xmlsec1 --version.
- On minimal Linux images, building cryptography (a pysaml2 dependency) from source needs a C compiler plus the libffi and OpenSSL development packages; without them the install dies with build errors that have nothing to do with saml2. Prefer a current pip and a Python version with prebuilt wheels (pysaml2 7.5.x requires Python 3.9.2 or newer per PyPI), or install the OS development packages first and retry.
- In Docker, install system packages and Python packages in the same image build, before copying in application code that imports saml2, and rebuild the image instead of installing into a running container that will be discarded.

Expected output: python -m pip install pysaml2 completes, and step 2 prints ok. Success check: scroll the install log; a warning is fine, an ERROR building a wheel is not, and a cached failed layer in Docker means you need a rebuild, not another shell install.

### 5. Rule out a local file shadowing the package

If your project contains a file named saml2.py, or a folder named saml2 without being the real library, Python finds yours first because the script directory and current directory come early on the import path. Symptoms differ slightly: you may still get No module named 'saml2' from a submodule import, an AttributeError for names the real package has, or a partially initialized module error on a circular import back into your own file.

```bash
python -c "import saml2; print(saml2.__file__)"
ls
```

Expected output: the printed path should be inside site-packages. If it points at a file in your project, rename that file (for example to saml_helpers.py), delete any stale compiled copy next to it, and update your imports. Also test from a different directory: if the import works elsewhere but not from the project root, shadowing is confirmed. Success check: step 2 passes from the project root with no local saml2 file present.

### 6. Pin it so the next environment does not regress

Add pysaml2 to the dependency file your deployment actually uses (requirements file, pyproject, Pipfile, or your framework's manifest), reinstall from scratch in a clean virtualenv, and run the step 2 check there. In CI and Docker this clean-room run is the proof; an import that only works on your laptop usually means a manual install that was never recorded.

Expected output: a fresh environment created only from the dependency file imports saml2 cleanly. Success check: deleting and recreating the virtualenv, then installing only from the dependency file, still prints ok in step 2.

## When this applies

- Code imports saml2, saml2.client, saml2.config, saml2.metadata, or names like BINDING_HTTP_REDIRECT, BINDING_HTTP_POST, or Saml2Client, and the run fails with ModuleNotFoundError or ImportError naming saml2.
- Fresh virtualenvs, Docker builds, CI runners, notebooks, or a newly deployed server, where other imports work but saml2 does not.
- pip reports pysaml2 as satisfied or already installed, yet the import fails in the process that actually runs the app.

## When this does not apply

- The error names onelogin (No module named 'onelogin'). That is python3-saml, a different library; its imports are onelogin.saml2. Install python3-saml for that code, not pysaml2.
- The import works and the failure is later: metadata parsing, certificate or key file errors, xmlsec signing failures, or IdP response validation. Those are configuration and runtime problems after installation; the xmlsec1 check in step 4 is relevant to signing failures, but this skill stops at a working import.
- You are on the Java, PHP, or JavaScript saml2 libraries with a similar name. This skill is only about the Python pysaml2 distribution.

### Variant: pysaml2 No module named saml2

The exact search phrasing behind the source thread. Same fix: steps 1 and 2, then step 3 if pip claimed it was already installed.

### Variant: No module named saml2 in Django, Flask, or djangosaml2

The framework changes nothing about the import; it only changes which interpreter runs it. Check the virtualenv the framework server was started with, and for djangosaml2 confirm pysaml2 is in the same environment as Django itself, using step 3.

### Variant: works in the terminal, fails under gunicorn, systemd, or cron

Those launchers do not inherit your interactive shell's virtualenv or path. Point the service at the virtualenv interpreter explicitly, or install into the interpreter named in the service definition, then restart the service.

### Variant: ImportError instead of ModuleNotFoundError

Same root causes on older Python versions and in some embedded interpreters. Work through steps 1 to 5 in order.

## Why this happens

Python import names and PyPI distribution names are independent. pysaml2 kept the short top-level package name saml2, so searching your installed list for saml2, or trying to install a package literally named saml2, misses the real distribution. On top of that, ModuleNotFoundError only ever means the running interpreter cannot find the package on its own search path: not installed at all, installed into a different interpreter or virtualenv, an install that failed partway on native prerequisites, or a local file taking the name first. The steps above separate those four causes instead of reinstalling blindly.

## Edge cases and pitfalls

- Two SAML libraries in one project: code importing both saml2 and onelogin.saml2 needs both pysaml2 and python3-saml installed. Installing one never provides the other.
- A stale requirements export from the wrong environment can omit pysaml2 even though it works locally. Compare python -m pip freeze in the working environment against the file you deploy.
- Upgrading Python can strand the old environment: a new interpreter (for example moving from 3.11 to 3.12) does not see packages installed for the old one. Recreate the virtualenv rather than copying site-packages across.
- Renaming a shadowing saml2.py is not enough if other code imports it by that name; rename the imports too, and clear stale bytecode caches if the old module keeps loading.
- In containers, a volume mounted over the application directory can hide or shadow site-packages installed in the image. Test the import with the same mounts the service runs with.
