Skip to main content

Pyrefly Configuration

Pyrefly has a basic configuration that can (or will) allow you to customize your Pyrefly runs without having to specify all of your arguments on the command line.

NOTE: this is early in its development, so the options listed here are subject to change in name, usage, type, quantity, and structure.

Configurations can be specified in a TOML file at the root of your project (or elsewhere, as long as the path-based config options point to the right place) named pyrefly.toml, with all configuration options in the top-level of the document. You can also specify a configuration in a pyproject.toml under a [tool.pyrefly] section. Other config names can be used when explicitly passing in the config file name with the --config/-c flag, but they will not be automatically found by Configuration Finding.

Both absolute and config-relative paths are supported.

Precedence in Options

Configuration options are selected in the following order

  1. CLI flags
  2. Examples: --project-excludes <value>, --python-version <value>
  3. Environment variable overrides -- this is the same as PYREFLY_<config option name>
  4. Examples: export PROJECT_EXCLUDES=<value>, PYTHON_VERSION=<value> pyrefly ...
  5. Configuration options
  6. Examples: (in a pyrefly.toml) project-excludes = <value>, python-version = <value>
  7. Pyrefly defaults
  8. See Default pyrefly.toml for the default values used

Type Checking Modes

Pyrefly has two different modes it can run in when type checking your project, which correspond to different but useful ways we expect most people to interact with Pyrefly:

  • Project mode: attempt to load a config, falling back to Pyrefly's default config when none can be found, and type check using that one config. This involves getting the project-includes and project-excludes from the file, expanding the patterns, and type checking on those files.
    • Project mode is used whenever no files are provided with the CLI invocation.
  • Per-file or Single-file mode: when given FILES... (and optionally --project-excludes) during a CLI invocation, expand the patterns and find the relevant config file for each file listed. project-includes and project-excludes are ignored from the config file, but it is used for all remaining config options.

Configuration Finding

In both project checking mode and single-file checking mode (see Type Checking Modes for more info), we attempt to find a project root from which to check each file, both for reading config options and for import resolution. The project root is typically the directory containing the configuration file. More precisely:

  1. If a configuration file is provided with -c/--config, we use the directory the file is located in as the directory to check.
  2. If no configuration file is passed, we perform an upward file search from the 'start location' to the filesystem root, looking in each directory for any of the following files: pyrefly.toml, pyproject.toml, setup.py, mypy.ini, and pyrightconfig.json. If we find one, we use the directory it's found in as the containing directory.
  3. If no configuration file is found, we will still attempt to resolve imports by walking up the tree looking for a matching import. For 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.

Note that only pyrefly.toml and pyproject.toml are parsed for config options, but we look for additional files that mark the root of a project to aid import resolution.

For project checking mode, the 'start location' is current working directory. For single-file checking mode, the start location is the directory containing each file to be type checked, and we find the config for each file matched by the pattern provided.

If a pyrefly.toml is found, it is parsed and used for type checking, and will return an error to the user on invalid types, syntax, values, or unknown config options.

If a pyproject.toml is found, Pyrefly will use the [tool.pyrefly] section if it exists, otherwise it will assume a default config. The same errors will be returned as when loading a pyrefly.toml if the config is invalid.

Providing a Config in Single-File Mode

Providing -c/--config in single-file checking mode disables the upward file search for config files. All options are read from the provided config file except project-includes and project-excludes, which are ignored.

Configuration Options

The following section lists all recognized options that can be specified in a config file or pyproject.toml Pyrefly config section.

project-includes

The glob patterns used to describe which files to type check, typically understood as user-space files.

This does not specify Import Resolution priority or the path an import should be resolved from. See search-path instead.

  • Type: list of filesystem glob patterns
  • Default: ["**/*.py", "**/*.pyi"]
  • Flag equivalent: FILES... argument
  • Equivalent configs: include in Pyright, files/modules/packages in mypy
  • Notes:
    • When overridden by passing in FILES..., we do not consult the relevant config file for what to use for project-excludes. If project-excludes should not use the default value, override it with the flag as well.
    • When a project-includes pattern does not match any files, we will return an error.
    • If you get an error about no matches for a directory when passing a glob as a CLI argument, try wrapping the glob in quotes to prevent eager shell glob expansion.

project-excludes

The glob patterns used to describe which files to avoid type checking as way to filter files that match project-includes, but we don't want to type check.

  • Type: list of filesystem glob patterns
  • Default: ["**/.[!/.]*", "**/*venv/**"]
  • Flag equivalent: --project-excludes
  • Equivalent configs: exclude in Pyright and mypy
  • Notes:
    • It is an error if no files are returned from any project-includes because they are filtered out by project-excludes entries. We differentiate between an error from a project-includes that doesn't match any files, and an error from all project-includes getting filtered by project-excludes.
    • When passing in FILES..., we also do not consult the config file for what to use for project-excludes. If project-excludes should not use the default value, override it with a flag as well. When no FILES... are passed, project-excludes overrides the config's value.
    • Your site-package-path is added to your project-excludes automatically. If you are trying to perform type checking on a dependency in your site-package-path (i.e. cd <site-package-path>/some_dependency && pyrefly check), we recommend you pull and set up your dependency from GitHub, but you can achieve this on files in your site-package-path by setting site-package-path = [] in your config.

search-path

A file path describing the roots from which imports should be found and imported from (including modules in project-includes). This takes the highest precedence in import order, before typeshed and site-package-path. When a project-includes type checked file is imported by another type checked file, we check all search roots to determine how to import it.

  • Type: list of directories specifying the root
  • Default: import root
  • Flag equivalent: --search-path
  • ENV equivalent: PYREFLY_SEARCH_PATH
  • Equivalent configs: extraPaths in Pyright, mypy_path in mypy
  • Notes:
    • We automatically append an import root (typically the directory containing the configuration file) to the search-roots when type checking as a sensible default and last attempt at an import.
    • Libraries should not be listed here, since they may override typeshed values for your whole project, and have different import semantics with respect to typing. See Import Resolution for more information about how modules are imported.

site-package-path

A file path describing a root from which imports should be found and imported from. This takes the lowest priority in import resolution, after search-path and typeshed.

site-package-path entries have special rules around when they can be imported, according to the typing specification. See Site Package Path Typing Rules for more information, and use-untyped-imports and ignore-missing-source for details on how to configure that behavior.

  • Type: list of directories
  • Default: result from Environment Autoconfiguration, or [] if the Python interpreter cannot be queried
  • Flag equivalent: --site-package-path
  • ENV equivalent: PYREFLY_SITE_PACKAGE_PATH
  • Equivalent configs: none

NOTE: Ideally, this should not be set manually, unless you're using a venv, running one-off tests, testing specific behavior, or having trouble with Environment Autoconfiguration. Setting this explicitly, especially when not using a venv, will make it difficult for your configuration to be reused between different systems and platforms.

python-platform

The value used with conditions based on type checking against sys.platform values.

  • Type: string
  • Default: result from Environment Autoconfiguration, or "linux" if the Python interpreter cannot be queried
  • Flag equivalent: --python-platform
  • ENV equivalent: PYREFLY_PYTHON_PLATFORM
  • Equivalent configs: pythonPlatform in Pyright, platform in mypy

python-version

The value used with conditions based on type checking against sys.version values. The format should be <major>[.<minor>[.<micro>]], where minor and micro can be omitted to take the default positional value.

  • Type: string of the format <major>[.<minor>[.<micro>]]
  • Default: result from Environment Autoconfiguration, or 3.13.0 if the Python interpreter cannot be queried
  • Flag equivalent: --python-version
  • ENV equivalent: PYREFLY_PYTHON_VERSION
  • Equivalent configs: pythonVersion in Pyright, python_version in mypy

python-interpreter

The Python interpreter to query when attempting to autoconfigure Python environment values (site-package-path, python-platform, python-version). See the Environment Autoconfiguration section for more information.

  • Type: path to executable
  • Default: $(which python3), then $(which python), or none
  • Flag equivalent: --python-interpreter
  • ENV equivalent: PYREFLY_PYTHON_INTERPRETER
  • Equivalent configs: python_executable in mypy
  • Notes:
    • This executes the value present in the python-interpreter field without any checks. It could be a security risk if your python-interpreter is an arbitrary executable.

NOTE: Ideally, this should not be set manually, unless you're using a venv, running one-off tests, testing specific behavior, or having trouble with Environment Autoconfiguration. Setting this explicitly, especially when not using a venv, will make it difficult for your configuration to be reused between different systems and platforms.

errors

Configure (enable and disable) the errors Pyrefly emits. true (default) tells Pyrefly to emit the error, while false tells Pyrefly to hide it.

  • Type: Table of error code to boolean representing enabled status
  • Default: errors = {}/[errors]
  • Flag equivalent: none
  • ENV equivalent: none
  • Equivalent configs: type check rule overrides and type evaluation settings in Pyright, enable_error_code and disable_error_code in mypy
  • Notes: setting <error-code> = true is the same as having no error code configuration present, which means the error will be shown. Setting <error-code> = false will disable the error for type checking.

replace-imports-with-any

ModuleGlobs of modules from which import errors should be ignored, and the module should be replaced with typing.Any. For example, with from x.y import z in a file, adding x.*, *.y, or x.y to this config will silence those import errors and replace the module with typing.Any. If the module can be found, its type information will still be replaced with typing.Any.

  • Type: list of regex
  • Default: []
  • Flag equivalent: none
  • ENV equivalent: none
  • Equivalent configs: ignore_missing_imports or follow_imports = skip in mypy
  • Notes:
    • errors = {import-error = false} (TOML inline table for errors) has similar behavior in Pyrefly, but ignores all import errors instead of import errors from specific modules, and won't replace findable modules with typing.Any.

ignore-errors-in-generated-code

Whether to ignore type errors in generated code. If enabled, generated files will be treated as if they are included in project-excludes. The generated code status is determined by checking if the file contents contain the substring '@generated'.

  • Type: bool
  • Default: false
  • Flag equivalent: --ignore-errors-in-generated-code
  • ENV equivalent: IGNORE_ERRORS_IN_GENERATED_CODE
  • Equivalent configs: none

use-untyped-imports

Whether to search imports in site-package-path that do not have a py.typed file unconditionally. When this is true, we skip any checks for py.typed files, and return the first valid import we can find. See Site Package Path Typing Rules for more information on when a site-package-path cannot be used for typing information.

  • Type: bool
  • Default: true
  • Flag equivalent: --use-untyped-imports
  • ENV equivalent: PYREFLY_USE_UNTYPED_IMPORTS
  • Equivalent configs: useLibraryCodeForTypes in Pyright, follow_untyped_imports in mypy

ignore-missing-source

Whether to skip the check for a non-stubs package when a -stubs package is found in site-package-path. When this is true, we immediately return a -stubs package when found.

The check for a non-stubs package exists to ensure you have an importable package to use in your project. When this check returns an import error, it means we couldn't find a package, and that at runtime you may not actually be able to import the given package, even if its type stubs could be found. See Stub Files vs Source Files for more information.

  • Type: bool
  • Default: true
  • Flag equivalent: none
  • ENV equivalent: none
  • Equivalent configs: reportMissingModuleSource in Pyright

untyped-def-behavior

How should Pyrefly treat function definitions with no parameter or return type annotations?

By default, Pyrefly uses the "check-and-infer-return-type" behavior and will check all function bodies inferring the return type.

If this option is set to "check-and-infer-return-any", then Pyrefly will still check the function body but will treat the return type as Any.

If this option is set to "skip-and-infer-return-any", Pyrefly will again treat the return type as Any, but will also skip checking the function body. In this case, Pyrefly will also infer Any as the type of any attributes inferred based on this function body. This behavior is what PEP 484 specifies, although we do not recommend it for most users today; since Pyrefly will not analyze the bodies of untyped functions, language server functionality like showing types on hover and finding definitions will not be available there.

  • Type: one of "check-and-infer-return-type", "check-and-infer-return-any", "skip-and-infer-return-any"
  • Default: `"check-and-infer-return-type"
  • Flag equivalent: none
  • ENV equivalent: none
  • Equivalent configs:
    • The "check-and-infer-return-type" behavior emulates Pyright's default behavior.
    • The "skip-and-infer-return-any" behavior emulates mypy's default behavior.
    • The "check-and-infer-return-any" behavior emulates mypy's check_untyped_defs flag.

sub-config

Override specific config values for matched paths in your project. See SubConfigs for more information on the structure and values that can be overridden here.

  • Type: TOML array of tables with a SubConfig structure
  • Default: []
  • Flag equivalent: none
  • ENV equivalent: none
  • Equivalent configs: executionEnvironments in Pyright, per-module config options in mypy

Configuration Details

This section describes some of the configuration options, behaviors, or types in more depth, when there are details shared between multiple config options or the information is more than what can fit under a single config option description.

Environment Autoconfiguration

If any of python-platform, python-version, or site-package-path are empty, we attempt to query an interpreter for the missing values.

We look for an interpreter with the following logic:

  1. Use python-interpreter if it's set by a flag or config option.
  2. Find a venv at the root of the project by searching for something that looks like a Python interpreter, and looking for a pyvenv.cfg file in known locations.
  3. Query $(which python3) and $(which python) (platform independent) to use a system-installed interpreter.
  4. Fall back to Pyrefly's default values for any unspecified config options.

The config options we query the interpreter for are:

  • python-platform: sys.platform
  • python-version: sys.version_info[:3]
  • site-package-path: site.getsitepackages() + [site.getusersitepackages()]

Filesystem Globbing

We use a standard Unix-style glob, which allows for wildcard matching when specifying a fileset. It is similar to regex, but more restricted given the subset of allowed syntax for paths on a filesystem. We currently only allow matching files with a .py or .pyi suffix.

The globs provided are relative to the config, if one is found, or the current working directory otherwise. Absolute path globs can also be provided, though this is generally not recommended, since it may not be compatible with other systems type checking your project.

  • We recognize the following wildcards:
    • * matches zero or more characters in a single directory component
    • ** matches the current and any sub directories/files in those sub directories
    • ? matches any one character
    • [<pattern>] matches any character or character range between the brackets (character range separated by -)
    • [!<pattern>] excludes any character or character range between the brackets and after the !
    • Note: [] can be used to match ?, *, [, ] literally (e.g. [?]), although these are invalid as part of a Python path.

We also support non-wildcard paths, so a relative (or absolute) path like src/ will match all Python files under src/ or src/my_file.py will match src/my_file.py exactly.

Any directories matched will also have their .py and .pyi files recursively matched. src/* will match all files and directories under src/, so therefore, we will recursively match everything under src/.

Examples:

  • src/**/*.py: only match .py files under src/
  • src, src/, src/*, src/**, and src/**/*: match all .py and .pyi files under `src/
  • ?.py and [A-z].py: match any file that looks like <letter>.py
  • src/path/to/my/file.py: only match src/path/to/my/file.py
  • src/**/tests, src/**/tests/, src/**/tests/**, and src/**/tests/**/*: match all .py and .pyi files in src/ under a directory named tests

Module Globbing

In some config options, we've added globbing for module paths. This is different from both path globs and regex, in the sense that we're performing a match on a Python dotted import, such as this.is.any.module. The only wildcard we recognize is *, which represents zero or more segments of a module path, unless it starts a glob, in which case it must match one or more segments. The wildcard must be surrounded by ., unless it is at the start or end of a module glob.

Examples:

  • this.is.a.module would be equivalent to a regex like ^this\.is\.a\.module. It will only match imports that look like this.is.a.module.
  • this.is.*.module would become ^this\.is(\..+)*\.module$. It would match:
    • this.is.module
    • this.is.a.module
    • this.is.a.really.long.path.to.a.module
  • *.my.module would be equivalent to a regex like ^.+\.my\.module$.
    • It would match:
      • this.is.my.module
      • heres.my.module
    • It will not match:
      • my.module
  • this.is.* would be equivalent to a regex like ^this\.is(\..+)*. It would match:
    • this.is.my.module
    • this.is

SubConfigs

SubConfigs are a method for overriding one or more config options for specific files based on filepath glob matching. Only certain config options are allowed to be overridden, and a need to override other configs means you likely need to use a separate config file for your subdirectory. You can have as many SubConfigs as you want in a project, and even multiple separate SubConfigs that can apply to a given file when the matches glob pattern matches.

SubConfig Allowed Overrides

We currently allow the following config options to be overridden in a SubConfig:

  • errors
  • replace-imports-with-any
  • untyped-def-behavior
  • ignore-errors-in-generated-code

All SubConfig overrides replace the values appearing in the 'root' or top-level of the Pyrefly configuration.

Any configs that change the list of files we're type checking, Python environment, or where we look for imports cannot be included in SubConfigs. Some other configs we also do not include because we think they make it difficult to reason about your project type checks, but you can open an issue or make a pull request if you disagree and would like to see the option supported.

SubConfig Table Structure

A SubConfig has two or more entries:

SubConfig Option Selection

Since you can have more than one SubConfig matching a file, we need to define a resolution order to determine which SubConfig's option should be selected. Pyrefly does this by filtering SubConfigs whose matches does not match the given file, then takes the first non-null value that can be found in the order the SubConfigs appear in your configuration.

If no SubConfigs match, or there are no non-null config options present, then we take the value in the 'root'/top-level Pyrefly config (or Pyrefly default if no value is specified).

Conda and Venv Support

We plan on adding extra automatic support for Conda and Venv at some point soon, but we haven't made it around to doing this yet. If you would like to import packages from these in the meantime, you can follow the following steps.

Venv

If you have a venv set up locally, you can get Pyrefly working with it by having your venv sourced in your shell (source .venv/bin/activate), and we will automatically pick up your installed packages. To pick up your packages even when your environment isn't sourced, you can add .venv/bin/python3 (or <path_to_venv>/bin/python3) to your Pyrefly configuration under python-interpreter or pass it in with the --python-interpreter flag.

Conda

If you have conda set up locally, you can get Pyrefly working with it by having your Conda environment sourced in your shell (conda activate <environment>), and we will automatically pick up your installed packages. To pick up your packages even when your environment isn't sourced, you can query your environment's install location with conda env list, and add <conda_environment_path>/bin/python3 to your Pyrefly configuration under python-interpreter or pass it in with the --python-interpreter flag.

Example Configuration

This section displays an example config showing the usage of all config options listed above to make creating your own easier, and to give you an easy place to start.

Default pyrefly.toml

This is a configuration with the Pyrefly defaults. If you have an interpreter installed, some of these values may be overridden.

###### configuring what to type check and where to import from

# check all files in "."
project-includes = ["."]
# exclude dotfiles
project-excludes = ["**/.[!/.]*", "**/*venv/**"]
# import project files from "."
search-path = ["."]
# do not include any third-party packages
site-package-path = []

###### configuring your python environment

# assume we're running on linux, regardless of the actual current platform
python-platform = "linux"
# assume the Python version we're using is 3.13, without querying an interpreter
python-version = "3.13"
# query the default Python interpreter on your system, if installed and `python_platform`,
# `python-version`, or `site-package-path` are unset.
# python-interpreter = null # this is commented out because there are no `null` values in TOML

#### configuring your type check settings

replace-imports-with-any = []
ignore-errors-in-generated-code = false
use-untyped-imports = false
ignore-missing-source = false

[errors]
# this is an empty table, meaning all errors are enabled by default

# no `[[sub-config]]` entries are included, since there are none by default

Example pyrefly.toml

project-includes = ["src"]
project-excludes = ["**/.[!/.]*", "**/tests"]
search-path = ["src"]
site-package-path = ["venv/lib/python3.12/site-packages"]

python-platform = "linux"
python-version = "3.12"
python-interpreter = "venv/bin/python3"

replace-imports-with-any = [
"sympy.*",
"*.series",
]
ignore-errors-in-generated-code = true
use-untyped-imports = true
ignore-missing-source = true

# disable `bad-assignment` and `invalid-argument` for the whole project
[errors]
bad-assignment = false
invalid-argument = false

[[sub-config]]
# apply this to `sub/project/tests/file.py`
matches = "sub/project/tests/file.py"

# any unittest imports will by typed as `typing.Any`
replace-imports-with-any = ["unittest.*"]

[[sub-config]]
# apply this config to all files in `sub/project`
matches = "sub/project/**"

# enable `assert-type` errors in `sub/project`
[sub-config.errors]
assert-type = true

[[sub-config]]
# apply this config to all files in `sub`
matches = "sub/**`

# disable `assert-type` errors in `sub`
[sub-config.errors]
assert-type = false

[[sub-config]]
# apply this config to all files under `tests` dirs in `sub/`
matches = "sub/**/tests/**"

# any pytest imports will be typed as `typing.Any`
replace-imports-with-any = ["pytest.*"]

With the above config, here are the actual config values some files would use:

  • sub/project/tests/file.py
    • replace-imports-with-any: ["unittest.*"]
    • errors: {assert-type = true}
    • ignore-errors-in-generated-code: true
    • use-untyped-imports: true
  • sub/project/tests/another_file.py
    • replace-imports-with-any: ["pytest.*"]
    • errors: {assert-type = true}
    • ignore-errors-in-generated-code: true
    • use-untyped-imports: true
  • sub/project/non_test_file.py
    • replace-imports-with-any: ["sympy.*", "*.series"]
    • errors: {assert-type = true}
    • ignore-errors-in-generated-code: true
    • use-untyped-imports: true
  • sub/sub_file.py
    • replace-imports-with-any: ["sympy.*", "*.series"]
    • errors: {assert-type = false}
    • ignore-errors-in-generated-code: true
    • use-untyped-imports: true
  • top_level_file.py
    • replace-imports-with-any: ["sympy.*", "*.series"]
    • errors: {assert-type = true, bad-assignment = false, invalid-argument = false}
    • ignore-errors-in-generated-code: true
    • use-untyped-imports: true

Example pyproject.toml

...

# Pyrefly header
[tool.pyrefly]

#### configuring what to type check and where to import from
project-includes = ["src"]
project-excludes = ["**/.[!/.]*", "**/tests"]
search-path = ["src"]
site-package-path = ["venv/lib/python3.12/site-packages"]

#### configuring your python environment
python-platform = "linux"
python-version = "3.12"
python-interpreter = "venv/bin/python3"

#### configuring your type check settings
replace-imports-with-any = [
"sympy.*",
"*.series",
]

ignore-errors-in-generated-code = true
use-untyped-imports = true
ignore-missing-source = true

[tool.pyrefly.errors]
bad-assignment = false
invalid-argument = false

[[tool.pyrefly.sub-config]]
# apply this config to all files in `sub/project`
matches = "sub/project/**"

# enable `assert-type` errors in `sub/project`
[tool.pyrefly.sub-config.errors]
assert-type = true

[[tool.pyrefly.sub-config]]
# apply this config to all files in `sub`
matches = "sub/**`

# disable `assert-type` errors in `sub/project`
[tool.pyrefly.sub-config.errors]
assert-type = false

# other non-Pyrefly configs
...