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:
- Try to import from each entry in
search_path
in the order they appear using the module finding strategy. - Try to import from
typeshed
. - Try to import implicit site packages. See the implicit site packages section
- Try to find a stub package corresponding to the import we're trying to resolve in
site_package_path
. Entries earlier in thesite_package_path
list will be selected before those appearing later in the list. - Try to find a non-stub package corresponding to the import we're trying to resolve in
site_package_path
. Entries earlier in thesite_package_path
list will be selected before those appearing later in the list. - 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.
- 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 asearch_path
entry. - 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 ofa.b.c
to find a root at/x/y/z
. - 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 - 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 apy.typed
file in their top-level containing the contentpartial\n
. - if we can't find any
-stubs
packages, only accept a package's types if it contains apy.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 anypy.typed
file. Check for-stubs
first and fall back to non-stubs, regardless of the presence of apy.typed
withpartial\n
or if the non-stubs packages contain apy.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.