Skip to main content

Import Resolution

This doc describes how imports in a given file are found within Pyrefly while performing a type check or resolving IDE language support operations.

NOTE: see the Configuration documentation for more info on the config options referenced below.

Relative Imports

If the import is relative (starting with one or more dots), the import is resolved relative to the path of the file importing it. A single dot at the beginning of the import (e.g. .file.to.import) represents the current directory, and more dots (e.g. ..other.file) will continue to walk upward.

Absolute Imports

For absolute imports, Pyrefly searches for a match in each of the following groups. The matching process is explained in the next paragraph.

  1. Try to import from the search path. See the search path section for more information.
  2. Try to import from typeshed.
  3. Try to import from the fallback search path. See the fallback search path section for more information on the contents of the search path.
  4. Try to import from the site package path. See the site package path section for more information on the contents of the site package path.
  5. Return an import error.

When searching for a match in one of the above groups, Pyrefly performs the following process over two passes, one looking for stub packages, and the other looking for source packages. See Stub Files vs Source Files for more information.

  1. Attempt to match each part of the name to directories in the group, selecting the first match that is found.
  2. If the result is a .pyi file or regular package (directory with an __init__.py/__init__.pyi file), return the result. Otherwise, keep searching and attempt to find a .pyi file or regular package.

Search Path

The search path (see search-path in configuration docs) consists of several entries representing project files.

  1. Search path from CLI args.
  2. Search path from config files.
  3. If disable-search-path-heuristics is not set, Pyrefly appends an import root directory to the search path.

The import root is:

  1. src/ if there's a src/ directory in the same directory as the config file.
  2. The parent directory (..) if there's an __init__.py or __init__.pyi in the same directory as the config file.
  3. Otherwise, the directory containing the config file.

Fallback Search Path

The fallback search path is a heuristic automatically constructed by Pyrefly to attempt to find project files when there's no config file marking the project root, and Pyrefly is unable to determine from other heuristics where an import root might be. It is only constructed when disable-search-path-heuristics is not set.

The fallback search path consists of each directory from the directory containing a given file to the root of your filesystem. For example, if you have the following setup:

/
|- projects/
|- project_a/
| |- b/
| | |- c.py
| |- d.py
|- project_e/
|- f.py

c.py's fallback search path would be ['/projects/project_a/b', '/projects/project_a', '/projects', '/']

  • d.py could be importable with the paths d, project_a.d, or projects.project_a.d.
  • f.py could be importable with the paths project_e.f or projects.project_e.f.

e.py's fallback search path would be ['/projects/project_a', '/projects', '/']

  • c.py could be importable with the paths b.c, project_a.b.c, or projects.project_a.b.c
  • f.py could be importable with the paths project_e.f or projects.project_e.f

f.py's fallback search path would be ['/projects/project_e', '/projects', '/']

  • c.py could be importable with the paths project_a.b.c or projects.project_a.b.c
  • d.py could be importable with the paths project_a.d or projects.project_a.d

Site Package Path

The site package path (see site-package-path in configuration docs) consists of several entries representing third-party packages.

  1. Site package path from a config file (if no CLI override is present) or CLI args.
  2. A site package path queried from a Python interpreter, if one could be found. See Environment Autoconfiguration for more information on finding interpreters.

Stub Files vs Source Files

A stub file is any file that ends with a .pyi file suffix. They have many uses, including adding typing to non-Python extension code, distributing typing information separate from implementation, or overriding an implementation with more accurate typing information.

A stub package is a second package corresponding to a regular package, with -stubs appended to its name. A -stubs package should only include stub files (.pyi), which override any .py or .pyi files in the non-stubs package. These are preferred when available, since they contain the interfaces a library exposes to developers. An example of this includes the popular library pandas, and its stub package, pandas-stubs.

When importing from a non-stubs package, Pyrefly loads typing information from imports by first searching for a relevant -stubs package, then by looking at the non-stubs package's .pyi files, then falls back to a .py file. See Absolute Imports for details on when non-stubs packages are allowed to be used for types, and how you can override that behavior.

Editable Installs

When using static analysis tools with an editable install, the editable install should be configured to use .pth files that contain file paths (/project/src/module) rather than executable lines (started with import) that install import hooks. See setuptools doc and PEP 660 for more information.

Import hooks can provide an editable installation that offers a more accurate representation of the actual installation environment. However, since resolving module locations through an import hook requires executing Python code at runtime, they are incompatible with Pyrefly and other static analysis tools that operate without code execution. Consequently, when an editable install is configured to use import hooks, Pyrefly will be unable to automatically locate and analyze the corresponding source files, resulting in incomplete type checking and code analysis.

Setuptools build system uses import hooks by default for editable installations. To ensure compatibility between setuptools-based editable installs and Pyrefly, setuptools must be configured to use path-based .pth files instead. This configuration should be performed through the build frontend (such as pip) by specifying the appropriate options during installation or in the project's configuration files.

uv with setuptools

When using uv with setuptools, uv can be configured to avoid import hooks.

NOTE: The uv_build backend always uses path-based .pth files.

pip with setuptools

When using pip with setuptools-based projects, there are two ways to avoid import hooks: compat mode and strict mode.

Hatch / Hatchling

Hatchling uses path-based .pth files by default. It will only use import hooks if you set dev-mode-exact to true.

PDM

PDM uses path-based .pth files by default. It will only use import hooks if you set editable-backend to "editables".

Poetry / Poetry-core

Poetry-core backend always uses path-based .pth files.

Debugging Import Issues

Pyrefly has a dump-config command that dumps the import-related config options it is using for each file it is checking. To use it, simply replace check with dump-config in your command-line invocation.