> ## Documentation Index
> Fetch the complete documentation index at: https://docs.endorlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> Learn how to implement Endor Labs in repositories with Python packages.

export const YamlTable = ({children, data: propData, content}) => {
  const KV_RE = /^([A-Za-z][A-Za-z0-9_()/#\s-]+?):\s*(.+)$/;
  const INLINE_MD_RE = /(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)/g;
  const YES_RE = /^-yes-$/i;
  const NO_RE = /^-no-$/i;
  const LIMITED_RE = /^-(limited|partial)-$/i;
  const NA_RE = /^-(na|none)-$/i;
  const NA2_RE = /^-na2-$/i;
  const SIMPLE_TAG_RE = /(<br\s*\/?>)|(<p\s*\/?>)|(-note-)|(-warning-)/gi;
  const tryParseKV = trimmed => {
    const m = KV_RE.exec(trimmed);
    return m ? {
      key: m[1],
      value: m[2].trim()
    } : null;
  };
  const registerKey = (key, seenKeys, orderedKeys) => {
    if (!seenKeys.has(key)) {
      orderedKeys.push(key);
      seenKeys.add(key);
    }
  };
  const flushEntry = (currentEntry, entries) => {
    if (Object.keys(currentEntry).length > 0) entries.push(currentEntry);
  };
  const parseDashPrefixed = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed.startsWith('- ')) {
        if (inEntry) entries.push(currentEntry);
        currentEntry = {};
        inEntry = true;
        const kv = tryParseKV(trimmed.substring(2).trim());
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      } else if (inEntry && trimmed !== '') {
        const kv = tryParseKV(trimmed);
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      }
    }
    flushEntry(currentEntry, entries);
  };
  const parseBlankSeparated = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed === '') {
        if (inEntry) {
          flushEntry(currentEntry, entries);
          currentEntry = {};
          inEntry = false;
        }
        continue;
      }
      const kv = tryParseKV(trimmed);
      if (!kv) continue;
      const isNewEntry = !line.startsWith(' ') && !line.startsWith('\t');
      if (isNewEntry && inEntry && Object.keys(currentEntry).length > 0) {
        entries.push(currentEntry);
        currentEntry = {};
      }
      registerKey(kv.key, seenKeys, orderedKeys);
      currentEntry[kv.key] = kv.value;
      inEntry = true;
    }
    flushEntry(currentEntry, entries);
  };
  const normalizeEntries = (entries, orderedKeys) => entries.map(entry => {
    const filled = {};
    for (const key of orderedKeys) filled[key] = entry[key] || '';
    return filled;
  });
  const parseYamlTableContent = contentStr => {
    if (!contentStr) return [];
    const entries = [];
    const orderedKeys = [];
    const seenKeys = new Set();
    const lines = contentStr.split('\n');
    if (lines.some(line => line.trim().startsWith('- '))) {
      parseDashPrefixed(lines, entries, orderedKeys, seenKeys);
    } else {
      parseBlankSeparated(lines, entries, orderedKeys, seenKeys);
    }
    return normalizeEntries(entries, orderedKeys);
  };
  const processText = text => {
    if (!text) return text;
    const parts = [];
    let keyIndex = 0;
    let lastIndex = 0;
    let match;
    while ((match = INLINE_MD_RE.exec(text)) !== null) {
      if (match.index > lastIndex) parts.push(text.slice(lastIndex, match.index));
      if (match[1]) {
        parts.push(<a key={keyIndex++} href={match[3]}>{match[2]}</a>);
      } else if (match[4]) {
        parts.push(<code key={keyIndex++}>{match[5]}</code>);
      } else if (match[6]) {
        parts.push(<strong key={keyIndex++}>{match[7]}</strong>);
      } else if (match[8]) {
        parts.push(<em key={keyIndex++}>{match[9]}</em>);
      }
      lastIndex = match.index + match[0].length;
    }
    if (lastIndex < text.length) parts.push(text.slice(lastIndex));
    if (parts.length === 0) return text;
    const keyRef = {
      current: keyIndex
    };
    return expandHtmlTags(parts, keyRef);
  };
  const processBadges = text => {
    if (!text || typeof text !== 'string') return text;
    if (YES_RE.test(text)) return <span className="yt-badge-yes" role="img" aria-label="Supported" title="Supported">✓</span>;
    if (NO_RE.test(text)) return <span className="yt-badge-no" role="img" aria-label="Not supported" title="Not supported">✗</span>;
    if (LIMITED_RE.test(text)) return <span className="yt-badge-limited" role="img" aria-label="Partially supported" title="Partially supported">◐</span>;
    if (NA_RE.test(text) || NA2_RE.test(text)) return <span className="yt-sr-only" title="Not applicable">Not applicable</span>;
    return processText(text);
  };
  const cellClassName = text => {
    if (!text || typeof text !== 'string') return undefined;
    if (NA_RE.test(text)) return 'yt-cell-na';
    if (NA2_RE.test(text)) return 'yt-cell-na2';
    return undefined;
  };
  const expandSimpleTags = (str, keyRef) => {
    const result = [];
    let last = 0;
    SIMPLE_TAG_RE.lastIndex = 0;
    let m;
    while ((m = SIMPLE_TAG_RE.exec(str)) !== null) {
      if (m.index > last) result.push(str.slice(last, m.index));
      if (m[1]) {
        result.push(<br key={keyRef.current++} />);
      } else if (m[2]) {
        result.push(<br key={keyRef.current++} />, <br key={keyRef.current++} />);
      } else if (m[3]) {
        result.push(<span key={keyRef.current++} className="yt-badge-note" style={{
          fontWeight: 600
        }}>Note: </span>);
      } else if (m[4]) {
        result.push(<span key={keyRef.current++} className="yt-badge-warning" style={{
          fontWeight: 600
        }}>Warning: </span>);
      }
      last = m.index + m[0].length;
    }
    if (last < str.length) result.push(str.slice(last));
    return result;
  };
  const expandHtmlTags = (chunks, keyRef) => {
    const out = [];
    for (const chunk of chunks) {
      if (typeof chunk === 'string') {
        out.push(...expandSimpleTags(chunk, keyRef));
      } else {
        out.push(chunk);
      }
    }
    return out;
  };
  const extractText = node => {
    if (node === null || node === undefined) return '';
    if (typeof node === 'string') return node;
    if (typeof node === 'number') return String(node);
    if (typeof node === 'boolean') return '';
    if (Array.isArray(node)) return node.map(extractText).join('');
    if (node && typeof node === 'object' && node.type) {
      const props = node.props || ({});
      if (typeof props.children === 'string') return props.children;
      if (props.children) return extractText(props.children);
      return '';
    }
    return String(node || '');
  };
  const [mounted, setMounted] = useState(false);
  useEffect(() => {
    setMounted(true);
  }, []);
  const data = useMemo(() => {
    if (propData) return propData;
    if (content && typeof content === 'string') return parseYamlTableContent(content);
    if (!children) return [];
    if (typeof children === 'string') return parseYamlTableContent(children);
    const childrenArray = Array.isArray(children) ? children : [children];
    return parseYamlTableContent(childrenArray.map(extractText).join('').trim());
  }, [children, propData, content]);
  const columns = useMemo(() => {
    if (!data || data.length === 0) return [];
    const firstRow = data[0];
    if (!firstRow || typeof firstRow !== 'object') return [];
    return Object.keys(firstRow);
  }, [data]);
  if (!mounted) return null;
  if (!data || data.length === 0) return null;
  const rowKey = row => columns.map(c => row[c] || '').join('|');
  return <table>
      <thead>
        <tr>
          {columns.map(col => <th key={col}>{col.replaceAll('_', ' ')}</th>)}
        </tr>
      </thead>
      <tbody>
        {data.map(row => <tr key={rowKey(row)}>
            {columns.map(col => <td key={col} className={cellClassName(row[col])}>{processBadges(row[col])}</td>)}
          </tr>)}
      </tbody>
    </table>;
};

Python is a high-level, interpreted programming language widely used by developers. Endor Labs supports the scanning and monitoring of Python projects managed by pip, Poetry, PDM, UV, Pipenv, or Bazel.

Using Endor Labs, application security engineers and developers can:

* Scan their software for potential security issues and violations of organizational policy.
* Prioritize vulnerabilities in the context of their applications.
* Understand the relationships between software components in their applications.

## System specifications for deep scan

Before you proceed to run a deep scan, ensure that your system meets the following specification.

| Project Size      | Processor         | Memory |
| ----------------- | ----------------- | ------ |
| Small projects    | 4-core processor  | 16 GB  |
| Mid-size projects | 8-core processor  | 32 GB  |
| Large projects    | 16-core processor | 64 GB  |

## Software prerequisites

Complete the following prerequisites:

* Install Python 3.6 or higher versions. Refer to the [Python documentation](https://www.python.org/downloads/) for instructions on how to install Python.
* For UV managed projects, install Python 3.8 or higher. To enable UV support, set the environment variable `ENDOR_SCAN_ENABLE_UV_PACKAGE_MANAGER=true`.
* Use the package manager [pip](https://pip.pypa.io/en/stable/installation/), [Poetry](https://python-poetry.org/docs/), [PDM](https://pdm-project.org/en/latest/), [UV](https://docs.astral.sh/uv/), or [Pipenv](https://pipenv.pypa.io/en/latest/installation.html#installing-pipenv) in your projects to build your software packages.
* If you are using pip with Python 3.12 or higher versions, install [setuptools](https://pypi.org/project/setuptools/).
* Set up any build, code generation, or other dependencies that your project's packages require.
* Organize the project as one or more packages using `setup.py`, `setup.cfg`, `pyproject.toml`, or `requirements.txt` package manifest files.
* Install Bazel version `5.x.x`, `6.x.x`, `7.x.x`, `8.x.x`, or `9.x.x` if your project uses Bazel. Endor Labs supports `rules_python` 0.9.0 up to (but not including) 0.30.0 with the WORKSPACE model only. Bzlmod requires `rules_python` 0.30.0 or higher and Bazel aspects. See [Bazel](/scan/bazel) for more information.
* Your repository must include a `WORKSPACE` file for WORKSPACE-based projects, or `MODULE.bazel` for Bzlmod projects when you use Bazel with Python.
* Make sure your repository includes one or more files with `.py` extension or pass either one of `requirements.txt`, `setup.py`, `setup.cfg` or `pyproject.toml` using the `--include-path` flag. See [Scoping scans](/best-practices/scoping-scans).

## Build Python projects

Create a virtual environment and build your Python projects before running the endorctl scan for the most accurate results. Endor Labs attempts to automatically create and configure a virtual environment when you do not provide one, but this may not work for complex projects. Verify that packages exist in the local package caches and build artifacts exist in the standard locations.

1. Configure any private repositories
   * If you use dependencies from a PyPI compatible repository *other than* `pypi.org`, configure it in the **Integrations** section of the Endor Labs web application. See [Configure private PyPI package repositories](#configure-private-pypi-package-repositories) for more details.
2. Clone the repository and optionally create a virtual environment inside it
   1. Clone the repository using `git clone` or an equivalent workflow.
   2. Enter the working copy root directory that's created.
   3. Create a virtual environment based on your package manager:

      For **pip or setuptools**

      * Use `python3 -m venv venv`. Set up the virtual environment in the root folder that you want to scan and name it `venv` or `.venv`.
      * Install your project's dependencies using `venv/bin/python -m pip install -r requirements.txt` or `venv/bin/python -m pip install`.
      * If you create the virtual environment outside the project, use one of the methods in [Virtual environment support](#virtual-environment-support) to specify the path of the Python virtual environment to Endor Labs.

      For **Poetry projects**

      * Install your project's dependencies using `poetry install`.

      For **PDM projects**

      * Install your project's dependencies using `pdm install`.

      For **Pipenv projects**

      * Run `pipenv install` in the project directory. This creates a `Pipfile.lock` (if it doesn't exist) and sets up a virtual environment while installing the required packages.

### Virtual environment support

Create a virtual environment to ensure consistent and accurate scan results and to verify that all dependencies install correctly before scanning. Automatic setup may encounter issues such as:

* Complex dependency chains or conflicting package requirements
* Private packages requiring authentication
* System-level dependencies not available in the scan environment
* Non-standard project structures or custom build scripts

Endor Labs attempts to automatically detect, create, or configure virtual environments for your projects. The behavior varies by package manager.

<AccordionGroup>
  <Accordion title="Poetry, Pipenv, and PDM">
    endorctl automatically detects and uses existing virtual environments managed by these tools.
  </Accordion>

  <Accordion title="UV">
    endorctl automatically creates a temporary virtual environment and deletes it after the scan completes. Install UV on your system for this automatic management to work.
  </Accordion>

  <Accordion title="pip">
    endorctl attempts to detect virtual environments in standard locations, such as `venv` or `.venv` directories in your project root. You can also use one of the following methods to specify the virtual environment:

    * Set up the virtual environment in the root folder that you want to scan and name it **venv** or **.venv**, it is automatically picked up by the Endor Labs application.

      ```bash theme={null}
      export PYTHONPATH=/usr/tmp/venv:/usr/tmp/another-venv
      ```

    * Set the environment variable `ENDOR_SCAN_PYTHON_VIRTUAL_ENV` to the path of the virtual environment of your Python project.

      ```bash theme={null}
      export ENDOR_SCAN_PYTHON_VIRTUAL_ENV=/usr/tmp/venv
      ```

    * Set the environment variable `ENDOR_SCAN_PYTHON_GLOBAL_SITE_PACKAGES` to true to indicate that a virtual environment is not present and Endor Labs can use the system-wide Python installation packages and modules.

      ```bash theme={null}
      export ENDOR_SCAN_PYTHON_GLOBAL_SITE_PACKAGES=true
      ```

    <Note>
      Setting both `ENDOR_SCAN_PYTHON_VIRTUAL_ENV` and `ENDOR_SCAN_PYTHON_GLOBAL_SITE_PACKAGES` environment variables at the same time is currently not supported, and the scan may not be successful.
    </Note>
  </Accordion>
</AccordionGroup>

If you do not set up the virtual environment, Endor Labs attempts to set it up with all the code dependencies. Install all dependencies in a virtual environment for the most accurate results.

If you use custom scripts without manifest files to assemble your dependencies, set up the virtual environment and install the dependencies first.

### Configure private PyPI package repositories

Endor Labs supports fetching and scanning dependencies from private PyPI package registries. Endor Labs will fetch resources from authenticated endpoints and perform the scan, allowing you to view the resolved dependencies and findings. See [PyPI package manager integrations](/integrations/package-managers/pypi-private-package-manager) for more information on configuring private registries.

## Scan Bazel projects

To scan Python projects that use Bazel, see [Bazel](/scan/bazel) for build instructions, supported rules, and scan commands. Endor Labs supports `rules_python` 0.9.0 up to (but not including) 0.30.0 with the WORKSPACE model only. Bzlmod requires `rules_python` 0.30.0 or higher with Bazel aspects. Software composition analysis includes reachability analysis. See [Bazel Aspects](/scan/bazel/bazel-aspects) for more information.

## Run a scan

Use the following options to scan your repositories. Perform the endorctl scan after building the projects.

### Option 1 - Quick scan

Perform a quick scan to get quick visibility into your software composition and perform dependency resolution. It discovers dependencies that the package has explicitly declared. If the package's build file is incomplete then the dependency list will also be incomplete. This scan won't perform reachability analysis to help you prioritize vulnerabilities.

```bash theme={null}
endorctl scan --quick-scan
```

You can perform the scan from within the root directory of the Git project repository, and save the local results to a *results.json* file. The results and related analysis information are available on the Endor Labs user interface.

```bash theme={null}
endorctl scan --quick-scan -o json | tee /path/to/results.json
```

Select **Projects** from the left sidebar, and find your project to review its results.

### Option 2 - Deep scan

Use the deep scan to perform dependency resolution, reachability analysis, and generate call graphs. You can do this after you complete the quick scan successfully.
The deep scan performs the following operations for the Python projects.

* Discovers explicitly declared dependencies,
* Discovers project dependent OSS packages present in the `venv/global` and `scope/python`.
* Performs reachability analysis and generates call graphs.
* Detects dependencies used in source code but not declared in the package's manifest files called `phantom dependencies`.

```bash theme={null}
endorctl scan
```

Use the following flags to save the local results to a *results.json* file. The results and related analysis information are available on the Endor Labs user interface.

```bash theme={null}
endorctl scan -o json | tee /path/to/results.json
```

During a deep scan, Endor Labs analyzes all private software dependencies in full by default if they have not been previously scanned. This is a one-time operation and will slow down initial scans, but won't impact subsequent scans.

Organizations might not own some parts of the software internally and the related findings are not actionable by them. They can choose to disable this analysis using the flag `disable-private-package-analysis`. By disabling private package analysis, teams can enhance scan performance but may lose insights into how applications interact with first-party libraries.

Select **Projects** from the left sidebar, and find your project to review its results.

## Understand the scan process

Endor Labs uses the following two methods to analyze your Python code.

* [Dependency resolution using manifest files](#dependency-resolution-using-manifest-files)
* [Dependency resolution using static analysis](#dependency-resolution-using-static-analysis)

Endor Labs uses the results from both these methods to perform superior dependency resolution, identify security issues, detect open-source vulnerabilities, and generate call graphs.

### Dependency resolution using manifest files

In this method, Endor Labs analyzes the manifest files present in a project to detect and resolve dependencies. Endor Labs analyzes the manifest files in the following priority.

<YamlTable>
  {`

    - Package_manager: Poetry
    Priority: 1
    Build_solution: \`poetry.lock\`,\`pyproject.toml\`
    - Package_manager: Pipenv
    Priority: 2
    Build_solution:\`Pipfile.lock\`,\`Pipfile\`
    - Package_manager: PDM
    Priority: 3
    Build_solution: \`pdm.lock\`,\`pyproject.toml\`
    - Package_manager: UV
    Priority: 4
    Build_solution: \`uv.lock\`,\`pyproject.toml\`
    - Package_manager: pip
    Priority: 5
    Build_solution: \`setup.py\`,\`setup.cfg\`,\`requirements.txt\`,\`pyproject.toml\`

    `}
</YamlTable>

For Poetry, PDM, and UV, when both `lock` and `toml` files are present, Endor Labs analyzes both files to detect and resolve dependencies.

For pip, Endor Labs analyzes the first available file in the priority list to detect and resolve dependencies, and ignores others.

<YamlTable>
  {`


    - Build_solution: \`setup.py\`
    Priority: 1
    - Build_solution: \`setup.cfg\`
    Priority: 2
    - Build_solution: \`pyproject.toml\`
    Priority: 3
    - Build_solution: \`requirements.txt\`
    Priority: 4


    `}
</YamlTable>

When a scan starts, Endor Labs identifies the package manager by inspecting files such as `pyproject.toml`, `poetry.lock`, `pdm.lock`, `setup.py`, and `requirements.txt`. If Endor Labs discovers `poetry.lock` or `pyproject.toml`, it uses Poetry to build the project. If it discovers `pdm.lock` or `pyproject.toml`, it uses PDM. Otherwise, it uses pip3.

#### Example

This is an example that demonstrates scanning a Python repository from GitHub on your local system using the **endorctl** scan.
This example assumes you run the scan on a Linux or Mac operating system and have the following Endor Labs API key and secret stored in environment variables. See [endorctl flags and variables](/developers-api/cli/environment-variables).

* `ENDOR_API_CREDENTIALS_KEY` set to the API key
* `ENDOR_API_CREDENTIALS_SECRET` set to the API secret
* `ENDOR_NAMESPACE` set to your namespace (you can find this when logged into Endor Labs by looking at your URL: `https://app.endorlabs.com/t/NAMESPACE/...`; it is typically a form of your organization's name)

##### pip

```python theme={null}
git clone https://github.com/HybirdCorp/creme_crm.git
cd creme_crm
python3 -m venv venv
source venv/bin/activate
venv/bin/python3 -m pip install
endorctl scan
```

##### Poetry

```python theme={null}
git clone https://github.com/HybirdCorp/creme_crm.git
cd creme_crm
poetry lock
endorctl scan
```

##### PDM

```python theme={null}
git clone https://github.com/HybirdCorp/creme_crm.git
cd creme_crm
pdm install
endorctl scan
```

##### UV

```python theme={null}
git clone https://github.com/example/repo.git
cd repo
endorctl scan
```

##### Pipenv

```python theme={null}
git clone https://github.com/example/repo.git
cd repo
pipenv install
endorctl scan
```

The scan for this repository should complete in a few minutes depending on the size of the project.
Select **Projects** from the left sidebar, and choose the **helloflas/flask-examples** project to see your scan results.

#### Handling custom and multiple requirement files in pip

Repositories often use non-standard names for pip requirement files (for example `default.txt`) or split dependencies across multiple `.txt` files. You can let Endor Labs discover those files automatically so you do not have to maintain a fixed list.

##### Auto detection of requirement files

Endor Labs can automatically detect alternate pip requirement files in your workspace. It identifies `.txt` files that follow pip requirements format using version specifiers and environment markers and treats them as manifests.

To enable auto detection, set `ENDOR_SCAN_ENABLE_PYTHON_REQUIREMENTS_AUTO_DETECT=true` and then run the endorctl scan.

```bash theme={null}
export ENDOR_SCAN_ENABLE_PYTHON_REQUIREMENTS_AUTO_DETECT=true
endorctl scan
```

##### Specify requirement files

To specify custom file names as requirement files, export the file name using the `ENDOR_SCAN_PYTHON_REQUIREMENTS` environment variable and then run the endorctl scan.

```bash theme={null}
export ENDOR_SCAN_PYTHON_REQUIREMENTS=default.txt
```

To resolve dependencies from multiple requirement files, export them as a comma-separated list using the `ENDOR_SCAN_PYTHON_REQUIREMENTS` environment variable and then run the endorctl scan.

```bash theme={null}
export ENDOR_SCAN_PYTHON_REQUIREMENTS=default.txt,requirements.txt
```

<Note>
  When you set the `ENDOR_SCAN_PYTHON_REQUIREMENTS` environment variable, Endor Labs considers only the file names specified in the variable for dependency analysis. For example, if you export `default.txt` and also have `requirements.txt` in your repository, `requirements.txt` is not included in the analysis.
</Note>

### Dependency resolution using static analysis

Not all Python projects include manifest files. A project can consist of a series of install statements that custom scripts assemble. Even when manifest files are present, the dependency information and version declared in the manifest file may differ drastically from what the project actually uses.

To solve this problem, Endor Labs performs a static analysis on the code, giving you complete visibility of what your code actually uses.

* Endor Labs enumerates all Python packages and recognizes the import statements within the project. An import statement brings external modules or libraries into your Python script.
* Endor Labs performs a static analysis of the code to match import statements with pre-installed packages and recursively traverses all files to create a dependency tree with the actual versions installed in the virtual environment.
* Endor Labs detects dependencies at the system level, identifies which ones resolve, and retrieves the precise name and version information from the library currently in use.
* Endor Labs gives you accurate visibility into your project components and helps you understand how they depend on one another.

Through this approach, Endor Labs conducts comprehensive dependency management, assesses reachability, and generates integrated call graphs.

<Note>
  Endor Labs performs dependency resolution using static analysis on deep scans only.
</Note>

### Known Limitations

* Endor Labs does not support Python versions older than 3.7, but they may work as expected.
* If you do not provide a virtual environment, Endor Labs does not assume Python version constraints based on the CI runtime environment. Dependencies appear for all possible Python versions at runtime. If you provide a virtual environment, Endor Labs respects what you installed in it.
* Symbolic links in manifest files may cause Endor Labs to duplicate the same package in the project.
* If a dependency is not available in the PyPI repository or in a configured private package repository, Endor Labs cannot build the software. Build the package in your local environment before scanning.
* Endor Labs treats a project as UV-managed if its `pyproject.toml` file contains the `tool.uv` key. Any member of a UV workspace is also UV-managed, even if its individual manifest file does not include the `tool.uv` key.
* When scanning UV workspaces, Endor Labs uses the workspace-level lock file for dependency resolution. Endor Labs does not scan individual workspace members as independent projects, ensuring consistency with UV's workspace architecture.
* Endor Labs does not currently detect inline script dependencies defined within Python script files during scanning.

#### Call Graph Limitations

* The call graph might not include function calls that use dispatch tables.
* The call graph might not include function calls that use unresolved variables.
* The call graph might not include dynamically modified or extended function calls that declare methods or attributes at runtime.
* The call graph might not include functions called indirectly through a function pointer rather than by their direct name.
* Install type stubs that provide hints or type annotations for functions, methods, and variables in your Python modules or libraries manually before scanning.
* If your project has a `pyproject.toml` file that includes `tools.pyright` section, it overrides Endor Labs settings for Pyright and may result in incorrect call graph results. You will need to remove the `tools.pyright` section from the `pyproject.toml` file.

## Troubleshoot errors

Here are a few error scenarios that you can check for and attempt to resolve them.

<AccordionGroup>
  <Accordion title="Virtual environment errors">
    You can identify the errors that may occur during virtual environment installation by looking for the following message in the error logs: *failed to create virtual environment* or *failed to install dependencies*.
  </Accordion>

  <Accordion title="Missing environment dependency">
    If your code depends on packages such as **psycopg2**, environment dependencies such as **PostgreSQL** are also required. The endorctl scan may fail if the environment where it is running does not have **PostgreSQL** installed.
  </Accordion>

  <Accordion title="Incompatible Python version">
    The default Python version in the scan environment is incompatible with one or more dependencies that the code needs.
  </Accordion>

  <Accordion title="Incompatible architecture">
    One or more dependencies are not compatible with the operating system architecture of the local system running the endorctl scan. For example, projects that depend on **PyObjC** run on Mac-based systems but not Linux systems. A few Python libraries are incompatible with x32 architectures and only run on x64 architectures.
  </Accordion>

  <Accordion title="Resolved dependency errors">
    A dependency version does not exist or cannot be found. The package may no longer exist in the repository.
  </Accordion>

  <Accordion title="Call graph errors">
    These errors occur if pip or Poetry cannot build the project because a required dependency is missing.
  </Accordion>
</AccordionGroup>
