astral-sh / ruff

An extremely fast Python linter and code formatter, written in Rust.

Home Page:https://docs.astral.sh/ruff

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Relative paths in `per-file-ignores` not respected when running `ruff` outside working directory using `--config` with `pyproject.toml`

mihirsamdarshi opened this issue · comments

Description

I noticed this (potential) issue while debugging koxudaxi/ruff-pycharm-plugin#359 for PyCharm. It's may be expected behavior, so please close this issue if so.

When running ruff from a directory that is not the same as the one containing the pyproject.toml file and specifying the --config option, the relative paths specified in the pyproject.toml's per-file-ignores configuration are not respected.

This normally isn't a problem, since the pyproject.toml lives in the root of project files, but I have a full-stack application where the python source code does not live in the source code (see the issue above). I open this root folder in PyCharm. Unfortunately, the ruff-pycharm-plugin runs ruff from the project's root directory, and therefore needs to pass the --config parameter.

Steps to Reproduce

  1. Create a new project, and install ruff

    ❯ rye init example-project
    ❯ cd example-project
    ❯ uv venv && source .venv/bin/activate # or `rye sync`
    ❯ uv pip install ruff
    ❯ example-project/.venv/bin/ruff --version
    ruff 0.4.10
  2. In example-project/pyproject.toml, configure per-file-ignores with relative paths:

    [tool.ruff.lint]
    # The default `rye` project will error on `D103` and `D104` when select = ["ALL"] is used
    select = ["ALL"]
    
    [tool.ruff.lint.per-file-ignores]
    "./src/example_project/__init__.py" = ["D104"]
  3. Deactivate the venv, verify that the ruff check command's output is correct from the project's working directory.

    ❯ deactivate
    ❯ .venv/bin/ruff check --config pyproject.toml src/
    warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `one-blank-line-before-class`.
    warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
    example-project/src/example_project/__init__.py:1:5: D103 Missing docstring in public function
    Found 1 error.
    
  4. Navigate up a directory, then run ruff check again

    cd ..
    ❯ example-project/.venv/bin/ruff check example-project/src/ --config example-project/pyproject.toml
    warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `one-blank-line-before-class`.
    warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
    example-project/src/example_project/__init__.py:1:1: D104 Missing docstring in public package
    example-project/src/example_project/__init__.py:1:5: D103 Missing docstring in public function
    Found 2 errors.

Environment

  • Ruff version: 0.4.10
  • OS: macOS 14.5
  • Shell: fish

Possible Solution

Consider adjusting how ruff resolves relative paths in the per-file-ignores configuration. It may need to use the directory of the pyproject.toml file as the base for resolving these paths, rather than the current working directory.

Thanks for the very detailed issue!

I had a quick look at the code and I can confirm that paths are always relative to the root directory.

extend_exclude: options
.extend_exclude
.map(|paths| {
paths
.into_iter()
.map(|pattern| {
let absolute = fs::normalize_path_to(&pattern, project_root);
FilePattern::User(pattern, absolute)
})
.collect()
})
.unwrap_or_default(),
extend_include: options
.extend_include
.map(|paths| {
paths
.into_iter()
.map(|pattern| {
let absolute = fs::normalize_path_to(&pattern, project_root);
FilePattern::User(pattern, absolute)
})
.collect()
})
.unwrap_or_default(),
include: options.include.map(|paths| {
paths
.into_iter()
.map(|pattern| {
let absolute = fs::normalize_path_to(&pattern, project_root);
FilePattern::User(pattern, absolute)
})
.collect()
}),
fix: options.fix,

I'm not entirely sure why the paths are relative to the root and not the configuration. @charliermarsh do you know more about the design decision?

@MichaReiser If I submitted a patch would that be helpful/accepted? Should I wait for @charliermarsh's input?

@mihirsamdarshi, I would prefer to hear @charliermarsh's opinion first. I'm not very familiar with that part of Ruff and the configuration schema is kind of complicated.