vscode-ruff icon indicating copy to clipboard operation
vscode-ruff copied to clipboard

Organize imports on save with custom config file location

Open toinbis opened this issue 1 year ago • 12 comments

I have issues with import formats on save not respect the ruff config file.

    "ruff.lint.args": [
        "--config=${workspaceFolder}/src/shared_core/static_configs/ruff.toml",
        "check",
        "--unsafe-fixes",
        "--fix"
    ],


    "ruff.format.args": [
        "--config=${workspaceFolder}/src/shared_core/static_configs/ruff.toml",
        "--preview"
    ],

    
    "[python]": {
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
          "source.fixAll": "always",
          "source.organizeImports": "always"
        },
        "editor.defaultFormatter": "charliermarsh.ruff"
      },

This is my vscode settings.json. I get consistent behaviour (=reading and using custom config file location) for:

  • ruff.lint.args & ruff format.args; VSCode highlights errors based on the config file;
  • The suggested fix dropdown: Screenshot 2024-02-20 at 14 41 12 It also fixes according to my config file.
  • The same command run from CLI also behaves identically.

I.e. All as expected!

Until I want to do organize imports automatically on save. So these settings come into effect:

"[python]": {
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
          "source.fixAll": "always",
          "source.organizeImports": "always"
        },
        "editor.defaultFormatter": "charliermarsh.ruff"
      }

And they seem to just use default ruff without any custom config. And it just formats differentely!

"ruff.lint.run": "onType|onSave", seemed like a setting which could help, but this settings, as shown by VSCode, is invalid.

How do I sort imports on save when using custom ruff config file location?

Thanks

toinbis avatar Feb 20 '24 14:02 toinbis

Thank you for opening the issue! Can you provide the content in your config file? And can you expand on how "it formats differently"? Like, what is the expected behavior and what are you actually getting?

dhruvmanila avatar Mar 18 '24 07:03 dhruvmanila

Hi, I'm having the same problem. It just started happening in the last week or so, not sure exactly when... Essentially the result of auto-formatting from VS Code is different to the result of running ruff check --fix on the CLI. From the ruff server logs I can see the packaged ruff version is 0.5.4. The version I have installed in my venv is 0.5.6. I have tried downgrading the version in my venv to 0.5.4 and the results are the same.

Here is my vscode settings file from the project folder.

{
    "python.defaultInterpreterPath": ".venv/bin/python",
    "python.envFile": "${workspaceFolder}/.env",
    "python.testing.unittestEnabled": false,
    "python.testing.pytestEnabled": true,
    "python.testing.autoTestDiscoverOnSaveEnabled": true,
    "python.testing.pytestArgs": [
        "-rs", "tests/"
    ],
    "editor.formatOnSave": false,
    "[python]": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.codeActionsOnSave": {
            "source.organizeImports": "explicit",
            "source.fixAll": "explicit"
        }
    },
    "mypy.configFile": "pyproject.toml",
    "mypy.targets": ["./tunedd_api", "./tests"],
    "mypy.runUsingActiveInterpreter": true,
}

And here are the ruff related settings from my pyproject file, which is at the root of my project folder.

[tool.ruff]  # Code formatting and linting
line-length = 100
src = ["tunedd_api", "tests"]
extend-exclude = ["__pycache__"]
output-format = "grouped"

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

[tool.ruff.lint]
select = ["D", "E", "F", "I", "W"]
ignore = ["D105", "D107", "D401", "E203", "E226", "E24", "E731", "E741", "F541", "F821", "W605"]

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.lint.isort]
known-first-party = ["tunedd_api"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]
no-lines-before = ["local-folder"]
force-sort-within-sections = true
combine-as-imports = true
split-on-trailing-comma = true
relative-imports-order = "furthest-to-closest"
lines-after-imports = 2

Here is an illustrative example of how the VS Code extension sorts the files on save. This appears to be consistent with the documentation for from-first which states that the default behaviour is to put from imports after regular imports. image

Now here is how it looks after running ruff check --fix on the CLI. This appears to be sorting the imports alphabetically without consideration of whether the import statement is a regular import or a from import. It is not apparent to me from the documentation that such ordering is a configurable possibility. image

Interestingly, if I add from-first = true or from-first = false to my pyproject.toml then it is completely ignored and the results are the same as those shown above in both cases. Other settings are ignored by the VS Code extension but honoured by ruff on the CLI (such as no-lines-before). I can confirm that the ruff extension is definitely working and respecting the config in the pyproject.toml file for settings unrelated to isort, but the isort settings seem to be ignored.

chrisk314 avatar Aug 07 '24 08:08 chrisk314

Essentially the result of auto-formatting from VS Code is different to the result of running ruff check --fix on the CLI.

Can you confirm that by "auto-formatting" you mean the Ruff formatter (ruff format) or the "Fix all" source action? I see that you've source.fixAll code action enabled on save.

dhruvmanila avatar Aug 07 '24 09:08 dhruvmanila

@dhruvmanila yes, by "auto-formatting" I mean the format on save actions configured in the vs code settings.json file which you mentioned.

chrisk314 avatar Aug 07 '24 09:08 chrisk314

That's editor.formatOnSave, right?

dhruvmanila avatar Aug 07 '24 09:08 dhruvmanila

I believe so. The project level settings.json file I have shared here is complete. There are no other formatting related settings in the user level settings.json file. I do not have any other extensions installed which would be formatting python files. So to the best of my knowledge all the automated formatting is applied as a result of editor.formatOnSave and editor.codeActionsOnSave configured here.

chrisk314 avatar Aug 07 '24 09:08 chrisk314

I do see the same formatting in the editor as well and I know why. It's because of the force-sort-within-sections which basically says that it doesn't look at the import style (import mod vs from mod import y) but it sorts the imports by module name.

https://github.com/user-attachments/assets/d9b9da50-0793-4699-bada-b0f027ad2b83

dhruvmanila avatar Aug 07 '24 09:08 dhruvmanila

OK so that clears up why from-first has no effect when I run ruff on the CLI. Thanks for testing and explaining that. So then the question is, why does the VS Code extension ignore the settings in my pyproject file? This appears to be a bug.

chrisk314 avatar Aug 07 '24 10:08 chrisk314

So then the question is, why does the VS Code extension ignore the settings in my pyproject file?

I'm not able to reproduce this. As you can see in the recording I've shared, it's formatting it as configured and as in your second screenshot.

In your first screenshot, I do see that the entire import block is highlighted which is likely to be from Ruff stating that the imports are un-organized.

You can turn on verbose logging which will provide log messages for all the request and response made by the client and server:

{
	"ruff.trace.server": "verbose"
}

If you can use this setting and then hit save in the file where the imports are unorganized, can you provide the log messages?

dhruvmanila avatar Aug 07 '24 11:08 dhruvmanila

OK here are the log messages from the ruff server. Here's what I did.

  1. Run ruff check --fix on the CLI which made my source file consistent with my pyproject.toml settings.
  2. Hit save in that source file in VS Code to trigger the formatOnSave / codeActionsOnSave actions which makes the source file inconsistent with my pyproject.toml settings.

Edit: I replaced my source file with a minimal reproducible example to reduce noise in the log file. Here's the example.

"""Tests for the DynamodbDatabaseService class."""

import typing as _t
from contextlib import nullcontext
from unittest.mock import patch

print(_t, nullcontext, patch)

Log output from Ruff server.

2024-08-07 12:52:40.068 [info] [Trace - 12:52:40 PM] Sending request 'textDocument/codeAction - (2118)'.
2024-08-07 12:52:40.068 [info] Params: {
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py"
    },
    "range": {
        "start": {
            "line": 0,
            "character": 0
        },
        "end": {
            "line": 8,
            "character": 0
        }
    },
    "context": {
        "diagnostics": [],
        "only": [
            "source.fixAll"
        ],
        "triggerKind": 2
    }
}


2024-08-07 12:52:40.069 [info] [Trace - 12:52:40 PM] Received response 'textDocument/codeAction - (2118)' in 2ms.
2024-08-07 12:52:40.069 [info] Result: [
    {
        "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
        "kind": "source.fixAll.ruff",
        "title": "Ruff: Fix all auto-fixable problems"
    }
]


2024-08-07 12:52:40.117 [info] [Trace - 12:52:40 PM] Sending request 'codeAction/resolve - (2119)'.
2024-08-07 12:52:40.117 [info] Params: {
    "title": "Ruff: Fix all auto-fixable problems",
    "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
    "kind": "source.fixAll.ruff"
}


2024-08-07 12:52:40.118 [info] [Trace - 12:52:40 PM] Received response 'codeAction/resolve - (2119)' in 1ms.
2024-08-07 12:52:40.118 [info] Result: {
    "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
    "edit": {
        "documentChanges": []
    },
    "kind": "source.fixAll.ruff",
    "title": "Ruff: Fix all auto-fixable problems"
}


2024-08-07 12:52:40.121 [info] [Trace - 12:52:40 PM] Sending request 'textDocument/codeAction - (2120)'.
2024-08-07 12:52:40.121 [info] Params: {
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py"
    },
    "range": {
        "start": {
            "line": 0,
            "character": 0
        },
        "end": {
            "line": 8,
            "character": 0
        }
    },
    "context": {
        "diagnostics": [],
        "only": [
            "source.organizeImports"
        ],
        "triggerKind": 2
    }
}


2024-08-07 12:52:40.121 [info] [Trace - 12:52:40 PM] Received response 'textDocument/codeAction - (2120)' in 0ms.
2024-08-07 12:52:40.121 [info] Result: [
    {
        "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
        "kind": "source.organizeImports.ruff",
        "title": "Ruff: Organize imports"
    }
]


2024-08-07 12:52:40.169 [info] [Trace - 12:52:40 PM] Sending request 'codeAction/resolve - (2121)'.
2024-08-07 12:52:40.169 [info] Params: {
    "title": "Ruff: Organize imports",
    "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
    "kind": "source.organizeImports.ruff"
}


2024-08-07 12:52:40.175 [info] [Trace - 12:52:40 PM] Received response 'codeAction/resolve - (2121)' in 6ms.
2024-08-07 12:52:40.175 [info] Result: {
    "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
    "edit": {
        "documentChanges": []
    },
    "kind": "source.organizeImports.ruff",
    "title": "Ruff: Organize imports"
}


2024-08-07 12:52:40.222 [info] [Trace - 12:52:40 PM] Sending notification 'textDocument/didChange'.
2024-08-07 12:52:40.222 [info] Params: {
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
        "version": 23
    },
    "contentChanges": [
        {
            "range": {
                "start": {
                    "line": 4,
                    "character": 31
                },
                "end": {
                    "line": 5,
                    "character": 0
                }
            },
            "rangeLength": 1,
            "text": ""
        },
        {
            "range": {
                "start": {
                    "line": 3,
                    "character": 0
                },
                "end": {
                    "line": 4,
                    "character": 0
                }
            },
            "rangeLength": 20,
            "text": ""
        },
        {
            "range": {
                "start": {
                    "line": 2,
                    "character": 0
                },
                "end": {
                    "line": 2,
                    "character": 0
                }
            },
            "rangeLength": 0,
            "text": "import typing as _t\n"
        }
    ]
}


2024-08-07 12:52:40.222 [info] [Trace - 12:52:40 PM] Sending request 'textDocument/formatting - (2122)'.
2024-08-07 12:52:40.222 [info] Params: {
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py"
    },
    "options": {
        "tabSize": 4,
        "insertSpaces": true,
        "trimTrailingWhitespace": true,
        "trimFinalNewlines": true,
        "insertFinalNewline": true
    }
}


2024-08-07 12:52:40.222 [info] [Trace - 12:52:40 PM] Sending request 'textDocument/diagnostic - (2123)'.
2024-08-07 12:52:40.222 [info] Params: {
    "identifier": "Ruff",
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py"
    }
}


2024-08-07 12:52:40.223 [info] [Trace - 12:52:40 PM] Received response 'textDocument/formatting - (2122)' in 1ms.
2024-08-07 12:52:40.223 [info] No result returned.


2024-08-07 12:52:40.223 [info] [Trace - 12:52:40 PM] Received response 'textDocument/diagnostic - (2123)' in 1ms.
2024-08-07 12:52:40.223 [info] Result: {
    "items": [
        {
            "code": "I001",
            "codeDescription": {
                "href": "https://docs.astral.sh/ruff/rules/unsorted-imports"
            },
            "data": {
                "code": "I001",
                "edits": [
                    {
                        "newText": "from contextlib import nullcontext\nimport typing as _t\nfrom unittest.mock import patch\n\n\n",
                        "range": {
                            "end": {
                                "character": 0,
                                "line": 6
                            },
                            "start": {
                                "character": 0,
                                "line": 2
                            }
                        }
                    }
                ],
                "kind": {
                    "body": "Import block is un-sorted or un-formatted",
                    "name": "UnsortedImports",
                    "suggestion": "Organize imports"
                },
                "noqa_edit": {
                    "newText": "  # noqa: I001\n",
                    "range": {
                        "end": {
                            "character": 0,
                            "line": 3
                        },
                        "start": {
                            "character": 19,
                            "line": 2
                        }
                    }
                }
            },
            "message": "Import block is un-sorted or un-formatted",
            "range": {
                "end": {
                    "character": 0,
                    "line": 6
                },
                "start": {
                    "character": 0,
                    "line": 2
                }
            },
            "severity": 2,
            "source": "Ruff"
        }
    ],
    "kind": "full"
}


2024-08-07 12:52:40.501 [info] [Trace - 12:52:40 PM] Sending request 'textDocument/codeAction - (2124)'.
2024-08-07 12:52:40.501 [info] Params: {
    "textDocument": {
        "uri": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py"
    },
    "range": {
        "start": {
            "line": 7,
            "character": 0
        },
        "end": {
            "line": 7,
            "character": 0
        }
    },
    "context": {
        "diagnostics": [],
        "triggerKind": 2
    }
}


2024-08-07 12:52:40.502 [info] [Trace - 12:52:40 PM] Received response 'textDocument/codeAction - (2124)' in 1ms.
2024-08-07 12:52:40.502 [info] Result: [
    {
        "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
        "kind": "source.fixAll.ruff",
        "title": "Ruff: Fix all auto-fixable problems"
    },
    {
        "data": "file:///home/csk/src/org/brainpool/tunedd/tunedd-api/tests/integration/issue_example.py",
        "kind": "source.organizeImports.ruff",
        "title": "Ruff: Organize imports"
    }
]

chrisk314 avatar Aug 07 '24 11:08 chrisk314

I managed to fix the problem by disabling and then re-enabling the Ruff extension. I didn't make any changes to the ruff config between the extension working prior to these issues, and the first occurence of these issues. So why it started doing this is a mystery.

Since these issues started I set "ruff.nativeServe": "on" in my user settings file, however my understanding is that this is the default in recent versions of the extension.

chrisk314 avatar Aug 07 '24 12:08 chrisk314

Thank you for providing the trace logs. It seems that the request - response is as expected but the server didn't provide any edits for the source.organizeImports code action which would've fixed the imports.

Since these issues started I set "ruff.nativeServe": "on" in my user settings file, however my understanding is that this is the default in recent versions of the extension.

The default value for that setting is "auto" which should pickup the native server automatically as your Ruff version is > 0.5.3 and there aren't any ruff.* settings that the native server doesn't support. https://github.com/astral-sh/ruff-vscode#using-the-rust-based-language-server provides some details on that.

It's good to know that it's working now. Thanks for following up on all the requested information. I really appreciate it :)

dhruvmanila avatar Aug 07 '24 17:08 dhruvmanila