Skip to main content

Import Resolution

This doc describes how imports in a given file are found and their bindings are resolved, including files that are being type checked.

NOTE: This document helps describe the internal behavior of Pyrefly and how we attempt to resolve imports. You should not need to know how this works to use Pyrefly but it may assist in debugging issues with Pyrefly not finding your imports. If you encounter a bug, please leave an issue for us on GitHub.

NOTE: see the Configuration documentation for more info on the config items 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 uses the following import strategy:

  1. Try to import from each entry in search_path in the order they appear using the module finding strategy.
  2. Try to import from typeshed.
  3. Try to import implicit site packages. See the implicit site packages section
  4. Try to find a stub package corresponding to the import we're trying to resolve in site_package_path. Entries earlier in the site_package_path list will be selected before those appearing later in the list.
  5. Try to find a non-stub package corresponding to the import we're trying to resolve in site_package_path. Entries earlier in the site_package_path list will be selected before those appearing later in the list.
  6. Return an import error.

See Site Package Path Typing Rules for more information about which modules are valid imports from site_package_path, and how to override that behavior.

Implicit Search Path

Pyrefly will use the following heuristics to guess the root of your project.

  1. If we can find a Pyrefly config file using configuration finding logic, we will automatically use that. When a configuration is found, we will add either a subdirectory named src/, or the found root as a search_path entry.
  2. If we're looking for a specific import and can match components of its specific name with directories/files on the filesystem, we will use the root of the import as the project root. Example: when importing from a.b.c import q, if our project structure looks like /x/y/z/a/b/c, we can walk up the components of a.b.c to find a root at /x/y/z.
  3. If the file we're importing is a non-namespace import, and we can't get it's parent directory, return a basic-default config with no heuristics. For example:
    # Assume we have a file called mymodule.py in the same directory
    import mymodule
  4. Finally, with either the non-namespace import's parent directory or namespace import's immediate directory, walk up the filesystem and add each directory we find until root to the fallback_search_path.

Site Package Path Typing Rules

When enabled with use_untyped_imports: false, we respect typing rules as defined by the typing spec for stubs packages, partial stubs packages, and py.typed files. That means:

  • if we can find any -stubs package, we do not fall back to non-stubs packages unless any of them contain a py.typed file in their top-level containing the content partial\n.
  • if we can't find any -stubs packages, only accept a package's types if it contains a py.typed file. Here, we only check for the existence of the file, not for any contents.

You can control the above behavior with the following two configs:

  • use_untyped_imports: don't worry about looking for any py.typed file. Check for -stubs first and fall back to non-stubs, regardless of the presence of a py.typed with partial\n or if the non-stubs packages contain a py.typed.
  • ignore_missing_source: don't try to check for a backing non-stubs package when we find a -stubs package. Immediately return the -stubs package when found.

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.

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.