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: 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. a. NOTE: we append the config file's directory to search_path automatically when using a config file as a sensible last-resort for attempting an import.
  2. Try to import from typeshed.
  3. 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. See the typing spec for more info on stub packages.
    1. If we find a -stubs package, but there's no non-stubs package, return an import error.
  4. 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.
  5. 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.

Site Package Path Typing Rules

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 pacakge. 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.