Skip to content

Add --project-dir to check the project of a directory - #5463

Open
kinto0 wants to merge 4 commits into
meta:mainfrom
kinto0:export-D128428212
Open

kinto0 wants to merge 4 commits into
meta:mainfrom
kinto0:export-D128428212

Conversation

@kinto0

@kinto0 kinto0 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Summary:
Stack context: let uv check run Pyrefly (astral-sh/uv#22941): check PEP 723 scripts in their own uv environments, and check each selected workspace member as a project. Example workspace:

pyproject.toml             # root member `demo`; [tool.pyrefly] project-excludes = ["**/vendor"]
src/demo/main.py
scripts/tool.py            # PEP 723 script: dependencies = ["six"]
packages/a/m.py            # member `a`, no Pyrefly config
packages/a/vendor/m.py
packages/b/pyproject.toml  # member `b`, with its own [tool.pyrefly]
packages/b/m.py
  • uv check --package demo runs pyrefly check --exclude-scripts --extend-project-excludes=packages/a --extend-project-excludes=packages/b --project-dir=.: it checks src/demo/main.py.

  • uv check --package a runs pyrefly check --exclude-scripts --project-dir=packages/a: it checks packages/a/m.py, not packages/a/vendor/m.py.

  • uv check --all-packages runs pyrefly check --exclude-scripts --project-dir=. --project-dir=packages/a --project-dir=packages/b: it checks each member with the config that governs it.

  • uv check --script scripts/tool.py runs pyrefly check --script-command=<uv> --script-command=workspace ... --script-command=--script -- scripts/tool.py: it resolves six from the script's own environment.

  • What: pyrefly check --project-dir <DIR> runs project mode for a directory. It finds the config from <DIR>, as pyrefly check does from the working directory, and checks the files that this config includes and that are under <DIR>. The flag repeats, so one run checks several projects. A file belongs to the deepest given directory that contains it, and is checked with the config of that directory.

  • Why: uv check selects workspace members by their root directories. With pyrefly check <paths>, Pyrefly ignores the project-excludes of the config, so uv check and pyrefly check disagree. Path mode cannot apply the config excludes in a clear way when configs nest or when an exclude matches a path that the user names. Project mode already has one clear rule: one config selects the files and checks them. --project-dir keeps that rule and only limits it to a directory, so a member gets the same files and the same config as in pyrefly check.

  • Why it works: most uv members have no [tool.pyrefly], so the config of a member is often the workspace root config. Each distinct config is walked once with its own includes and excludes, and a file is kept only by the config of the directory that it belongs to. The config finder gives each file under a directory that same config, and gives other files (dependencies) the config of the normal search.

Example: pyrefly check --project-dir=packages/a checks packages/a/m.py with the root config, so packages/a/vendor/m.py stays excluded. pyrefly check --project-dir=. --project-dir=packages/a --project-dir=packages/b (what uv check --all-packages runs) checks src/demo/main.py and packages/a/m.py with the root config, and packages/b/m.py with the config of packages/b. Before, uv ran pyrefly check packages/a, which also checked packages/a/vendor/m.py.

Differential Revision: D128428212

Summary:
Stack context: let `uv check` run Pyrefly (astral-sh/uv#22941): check PEP 723 scripts in their own uv environments, and check each selected workspace member as a project. Example workspace:
```
pyproject.toml             # root member `demo`; [tool.pyrefly] project-excludes = ["**/vendor"]
src/demo/main.py
scripts/tool.py            # PEP 723 script: dependencies = ["six"]
packages/a/m.py            # member `a`, no Pyrefly config
packages/a/vendor/m.py
packages/b/pyproject.toml  # member `b`, with its own [tool.pyrefly]
packages/b/m.py
```
- `uv check --package demo` runs `pyrefly check --exclude-scripts --extend-project-excludes=packages/a --extend-project-excludes=packages/b --project-dir=.`: it checks `src/demo/main.py`.
- `uv check --package a` runs `pyrefly check --exclude-scripts --project-dir=packages/a`: it checks `packages/a/m.py`, not `packages/a/vendor/m.py`.
- `uv check --all-packages` runs `pyrefly check --exclude-scripts --project-dir=. --project-dir=packages/a --project-dir=packages/b`: it checks each member with the config that governs it.
- `uv check --script scripts/tool.py` runs `pyrefly check --script-command=<uv> --script-command=workspace ... --script-command=--script -- scripts/tool.py`: it resolves `six` from the script's own environment.

- Add `pyrefly check --exclude-scripts`. Pyrefly skips the files with a PEP 723 `# /// script` block that it finds in the project or in the given directories, and still checks files given explicitly.
- Pyrefly finds a script by its `# /// script` metadata block, which `script::metadata` parses.
- `uv check` checks a project without its scripts, which must be checked with `uv check --script`. ty finds the scripts in its own walk and skips them with the same flag, so uv only passes the flag. Without it, uv must find the scripts itself and pass each one as an exclude.

Example: `pyrefly check --exclude-scripts` checks `src/demo/main.py` and `packages/*/m.py`, and skips `scripts/tool.py`. `pyrefly check --exclude-scripts -- . scripts/tool.py` still checks `scripts/tool.py`, because it is given explicitly.

Reviewed By: rchen152

Differential Revision: D128114982
Summary:
Stack context: let `uv check` run Pyrefly (astral-sh/uv#22941): check PEP 723 scripts in their own uv environments, and check each selected workspace member as a project. Example workspace:
```
pyproject.toml             # root member `demo`; [tool.pyrefly] project-excludes = ["**/vendor"]
src/demo/main.py
scripts/tool.py            # PEP 723 script: dependencies = ["six"]
packages/a/m.py            # member `a`, no Pyrefly config
packages/a/vendor/m.py
packages/b/pyproject.toml  # member `b`, with its own [tool.pyrefly]
packages/b/m.py
```
- `uv check --package demo` runs `pyrefly check --exclude-scripts --extend-project-excludes=packages/a --extend-project-excludes=packages/b --project-dir=.`: it checks `src/demo/main.py`.
- `uv check --package a` runs `pyrefly check --exclude-scripts --project-dir=packages/a`: it checks `packages/a/m.py`, not `packages/a/vendor/m.py`.
- `uv check --all-packages` runs `pyrefly check --exclude-scripts --project-dir=. --project-dir=packages/a --project-dir=packages/b`: it checks each member with the config that governs it.
- `uv check --script scripts/tool.py` runs `pyrefly check --script-command=<uv> --script-command=workspace ... --script-command=--script -- scripts/tool.py`: it resolves `six` from the script's own environment.

- What: a scrut test with a uv-style workspace. It shows that in project mode `--project-excludes` replaces the `project-excludes` of the config.
- Why: `uv check` must exclude the workspace members that it does not select, and keep the user's own excludes. This test shows that `--project-excludes` cannot do that. The next diff adds `--extend-project-excludes` and tests it in this workspace.

Example: `pyrefly check --project-excludes=packages/b` checks `packages/a/vendor/m.py`, because the flag replaces the root `project-excludes = ["**/vendor"]`.

Differential Revision: D128428110
Summary:
Stack context: let `uv check` run Pyrefly (astral-sh/uv#22941): check PEP 723 scripts in their own uv environments, and check each selected workspace member as a project. Example workspace:
```
pyproject.toml             # root member `demo`; [tool.pyrefly] project-excludes = ["**/vendor"]
src/demo/main.py
scripts/tool.py            # PEP 723 script: dependencies = ["six"]
packages/a/m.py            # member `a`, no Pyrefly config
packages/a/vendor/m.py
packages/b/pyproject.toml  # member `b`, with its own [tool.pyrefly]
packages/b/m.py
```
- `uv check --package demo` runs `pyrefly check --exclude-scripts --extend-project-excludes=packages/a --extend-project-excludes=packages/b --project-dir=.`: it checks `src/demo/main.py`.
- `uv check --package a` runs `pyrefly check --exclude-scripts --project-dir=packages/a`: it checks `packages/a/m.py`, not `packages/a/vendor/m.py`.
- `uv check --all-packages` runs `pyrefly check --exclude-scripts --project-dir=. --project-dir=packages/a --project-dir=packages/b`: it checks each member with the config that governs it.
- `uv check --script scripts/tool.py` runs `pyrefly check --script-command=<uv> --script-command=workspace ... --script-command=--script -- scripts/tool.py`: it resolves `six` from the script's own environment.

- What: `--extend-project-excludes` adds patterns to the excludes that apply. In project mode, that is the `project-excludes` of the config (or `coverage.excludes` for `pyrefly coverage`). When files are given, that is `--project-excludes` plus the default excludes. `--project-excludes` still replaces the configured excludes.
- Why: `uv check` must exclude the workspace members that it does not select, and keep the user's own excludes. `--project-excludes` drops them. A separate flag does not change the behavior of `--project-excludes` for existing users. Ruff has the same pair: `--exclude` and `--extend-exclude`.

Example: `pyrefly check --extend-project-excludes=packages/b` checks `src/demo/main.py` and `packages/a/m.py`. It skips `packages/b`, and it skips `packages/a/vendor/m.py` because the root exclude still applies.

Differential Revision: D128428167
Summary:
Stack context: let `uv check` run Pyrefly (astral-sh/uv#22941): check PEP 723 scripts in their own uv environments, and check each selected workspace member as a project. Example workspace:
```
pyproject.toml             # root member `demo`; [tool.pyrefly] project-excludes = ["**/vendor"]
src/demo/main.py
scripts/tool.py            # PEP 723 script: dependencies = ["six"]
packages/a/m.py            # member `a`, no Pyrefly config
packages/a/vendor/m.py
packages/b/pyproject.toml  # member `b`, with its own [tool.pyrefly]
packages/b/m.py
```
- `uv check --package demo` runs `pyrefly check --exclude-scripts --extend-project-excludes=packages/a --extend-project-excludes=packages/b --project-dir=.`: it checks `src/demo/main.py`.
- `uv check --package a` runs `pyrefly check --exclude-scripts --project-dir=packages/a`: it checks `packages/a/m.py`, not `packages/a/vendor/m.py`.
- `uv check --all-packages` runs `pyrefly check --exclude-scripts --project-dir=. --project-dir=packages/a --project-dir=packages/b`: it checks each member with the config that governs it.
- `uv check --script scripts/tool.py` runs `pyrefly check --script-command=<uv> --script-command=workspace ... --script-command=--script -- scripts/tool.py`: it resolves `six` from the script's own environment.

- What: `pyrefly check --project-dir <DIR>` runs project mode for a directory. It finds the config from `<DIR>`, as `pyrefly check` does from the working directory, and checks the files that this config includes and that are under `<DIR>`. The flag repeats, so one run checks several projects. A file belongs to the deepest given directory that contains it, and is checked with the config of that directory.
- Why: `uv check` selects workspace members by their root directories. With `pyrefly check <paths>`, Pyrefly ignores the `project-excludes` of the config, so `uv check` and `pyrefly check` disagree. Path mode cannot apply the config excludes in a clear way when configs nest or when an exclude matches a path that the user names. Project mode already has one clear rule: one config selects the files and checks them. `--project-dir` keeps that rule and only limits it to a directory, so a member gets the same files and the same config as in `pyrefly check`.
- Why it works: most uv members have no `[tool.pyrefly]`, so the config of a member is often the workspace root config. Each distinct config is walked once with its own includes and excludes, and a file is kept only by the config of the directory that it belongs to. The config finder gives each file under a directory that same config, and gives other files (dependencies) the config of the normal search.

Example: `pyrefly check --project-dir=packages/a` checks `packages/a/m.py` with the root config, so `packages/a/vendor/m.py` stays excluded. `pyrefly check --project-dir=. --project-dir=packages/a --project-dir=packages/b` (what `uv check --all-packages` runs) checks `src/demo/main.py` and `packages/a/m.py` with the root config, and `packages/b/m.py` with the config of `packages/b`. Before, uv ran `pyrefly check packages/a`, which also checked `packages/a/vendor/m.py`.

Differential Revision: D128428212
@meta-cla meta-cla Bot added the cla signed label Oct 30, 2026
@meta-codesync

meta-codesync Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

@kinto0 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D128428212.

@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

1 participant