Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: A file is reformatted via VS Code but not via CLI #318

Closed
thernstig opened this issue Oct 27, 2023 · 17 comments · Fixed by #321
Closed

Bug: A file is reformatted via VS Code but not via CLI #318

thernstig opened this issue Oct 27, 2023 · 17 comments · Fixed by #321
Labels
question Asking for support or clarification

Comments

@thernstig
Copy link

The following does nothing:

ruff format tests/utils/mongo.py

But saving the same file in VS Code does. (It removes a line break between some import statements).

Output from VS Code Ruff:

2023-10-27 10:40:32.583 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.583 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.583 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--fix', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']
2023-10-27 10:40:32.588 [info] []
2023-10-27 10:40:32.616 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.617 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.617 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--fix', '--extend-ignore', 'ALL', '--extend-select', 'I001', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']
2023-10-27 10:40:32.626 [info] []
2023-10-27 10:40:32.693 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.693 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.693 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']
2023-10-27 10:40:32.699 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.699 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.699 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['format', '--force-exclude', '--quiet', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']
2023-10-27 10:40:32.885 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.885 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.885 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']

Ruff settings:

[tool.ruff]
cache-dir = "/tmp/.cache/ruff"
line-length = 120

# All uncommented lines should be fixed and enabled
select = [
  # These are a copy of the Ruff defaults
  # "F" is Pyflakes, "E" is pycodestyle
  "E4", "E7", "E9", "F",
  "A", # flake8-builtins
  # "B", # flake8-bugbear
  # "C4", # flake8-comprehensions
  "C90", # mccabe
  # "COM", # flake8-commas
  "D", # pydocstyle
  # "FIX", # flake8-fixme
  # "RUF100", # Ruff rule: Enforce that noqa directives are "valid"
  # "S", # flake8-bandit
  "TID", # flake8-tidy-imports
]

ignore = [
  # All these ignores should be fixed
  "D10",  # pydocstyle "Missing docstring in ..."
  "D203", # pydocstyle "one-blank-line-before-class" conflicting with D211
  "D205", # pydocstyle "1 blank line required between summary line and description"
  "D208", # pydocstyle "Docstring is over-indented"
  "D209", # pydocstyle "Multi-line docstring closing quotes should be on a separate line"
  "D211", # pydocstyle "no-blank-line-before-class" conflicting with D203
  "D212", # pydocstyle "multi-line-summary-second-line" conflicting with D213
  "D213", # pydocstyle "no-blank-line-before-class" conflicting with D212
  "D400", # pydocstyle "First line should end with a period"
  "D401", # pydocstyle "First line of docstring should be in imperative mood"
  "D404", # pydocstyle "First word of the docstring should not be "This""
  "D403", # pydocstyle "First word of the first line should be capitalized"
  "D407", # pydocstyle "Missing dashed underline after section ("Return")"
  "D415", # pydocstyle "First line should end with a period, question mark, or exclamation point"
  "D419", # pydocstyle "Docstring is empty"
  "E713", # pycodestyle "Test for membership should be `not in`"
  # These are disabled to not conflict with the Ruff formatter
  "COM812", # flake8-commas - missing-trailing-comma
  "COM819", # flake8-commas - prohibited-trailing-comma
  "D206", # pydocstyle - indent-with-spaces
  "D300", # pydocstyle - triple-single-quotes
]

[tool.ruff.format]
quote-style = "single"

[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"

VS Code settings:

{
  "editor.codeActionsOnSave": {
    "source.organizeImports": "always",
    "source.fixAll.eslint": "always",
    "source.fixAll.markdownlint": "always",
    "source.fixAll.stylelint": "always",
    "source.fixAll.ruff": "always"
  },
  "editor.formatOnPaste": true,
  "editor.formatOnSave": true,
  "editor.suggest.localityBonus": true,
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "python.analysis.typeCheckingMode": "basic",
  "python.analysis.completeFunctionParens": true,
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
}
@MichaReiser MichaReiser added the question Asking for support or clarification label Oct 27, 2023
@MichaReiser
Copy link
Member

What version does ruff version print? Any chance it is different from the version VS code uses (seems to be 0.1.2)?

@thernstig
Copy link
Author

thernstig commented Oct 27, 2023

> ruff version
ruff 0.1.2

Since I have not overriden importStrategy it is fromEnvironment and the Ruff output from VS Code says /home/userA/code/foo/.venv/bin/ruff so that also confirms it is using my .venv/ dir.

I can mention I checked the Python output channel in VS Code as well to make sure it did not format anything, and it had no output so I would expect it did not. Especially since I set:

  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  },

Is it possible this could be a VS Code Python extension problem? I reported another problem they had in microsoft/vscode-python#22338 but it should have been fixed.

@buhrmann
Copy link

I seem to have the "opposite" problem, no formatting happens when I save a file in VS Code with the following snippet:

def set_metadata(obj: Pa | Pd, 
meta: Metadata, 
    clear: bool = False) -> Pa | Pd:
    """Store table- and column-level met ...

The linter complains, but it will only format if I explicitly use the Ruff: Format document command.

The log when saving is something like

2023-10-27 17:09:09.972 [info] [Trace - 17:09:09] Received notification 'window/logMessage'.
2023-10-27 17:09:09.972 [info] Running Ruff with: /Users/thomas/micromamba/envs/grp/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--stdin-filename', '/Users/thomas/code/cocktail/base_images/repos/grapy/libs/core/grapy/core/io/arrow.py']
2023-10-27 17:09:09.988 [info] [Trace - 17:09:09] Received notification 'textDocument/publishDiagnostics'.

While the log when explicitly formatting is

023-10-27 17:07:42.742 [info] [Trace - 17:07:42] Received notification 'window/logMessage'.
2023-10-27 17:07:42.742 [info] Running Ruff with: /Users/thomas/micromamba/envs/grp/bin/ruff ['format', '--force-exclude', '--quiet', '--stdin-filename', '/Users/thomas/code/cocktail/base_images/repos/grapy/libs/core/grapy/core/io/arrow.py']
2023-10-27 17:07:42.762 [info] [Trace - 17:07:42] Received request 'workspace/applyEdit - (b7f0e57a-f414-4b23-bd7d-f66e1e2f5b79)'.
2023-10-27 17:07:42.763 [info] [Trace - 17:07:42] Received response 'workspace/executeCommand - (130)' in 24ms.
2023-10-27 17:07:42.772 [info] [Trace - 17:07:42] Sending notification 'textDocument/didChange'.

This is with the lastest versions of Ruff and extension, and everything enables, that I can think of:

"[python]": {
        "editor.formatOnSave":true,
        "editor.formatOnPaste": true,
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.tabSize": 4,
        "editor.insertSpaces": true,
        "editor.codeActionsOnSave": {
            "source.fixAll":"always",
            "source.organizeImports": true
        },
        "python.linting.enabled": true,
        "python.linting.lintOnSave": true,
    },

@charliermarsh
Copy link
Member

Hmm, that's a tricky one. Your configuration does look right, and if something were going wrong, I'd expect to see an error in the log (thank you for including those). You don't happen to have ruff.format.run set to onSave, do you?

@buhrmann
Copy link

buhrmann commented Oct 28, 2023

It's working now, after a restart. I'm pretty sure I'd already tried restarting VS Code, so not sure what changed. Maybe a subtle bug in the Python plugin (t also e.g. tries to run flake8 all the time, even though it's neither installed nor configured anywhere...)

Anyway, successfully moved from black to Ruff 😃

@thernstig
Copy link
Author

Here is a minimal repo with the Python package pymongo. I would assume VS Code wants a line break due to logging being a standard lib whilst pymongo is an installed package:

import logging
import pymongo

logger = logging.getLogger(__name__)

pymongo.MongoClient([])

VS Code will add a line break between the two import statements whilst Ruff via CLI will not.

I disabled all extensions, and enabled them one by one. Start with the Python extension (which also installed Pylance). The formatting was not done to the above.

I then enabled the Ruff extension, and now the formatting was done to the file.

So it proves it is the Ruff extension doing this.

@charliermarsh
Copy link
Member

Can you try running the Organize imports code action in VS Code with Ruff installed? Import sorting in this way is treated as a separate action from code formatting.

@thernstig
Copy link
Author

thernstig commented Oct 30, 2023

Organize Imports does add the line break.

Also, when testing this from the VS Code output channel for Ruff, it also adds the line break:

ruff --force-exclude --no-cache --no-fix --quiet --output-format json --fix --extend-ignore 'ALL' --extend-select 'I001' /home/userA/code/foo/tests/utils/mongo.py

So this seems to be the culprit.

Why is it running ruff multiple times on one save, with all these various extra things added? Should it just not respect pyproject.toml if it exists (or its equivalents)?

@charliermarsh
Copy link
Member

Are you running Ruff via the CLI with --extend-select ["I"] or similar? Import sorting has to be enabled, it's off-by-default.

@thernstig
Copy link
Author

thernstig commented Oct 30, 2023

@charliermarsh now I am a bit confused.

When I run ruff locally I always do this with no parameters. But it is using the pyproject.toml as in the initial post:
ruff format .

That does not format/add the line break.

But saving in VS Code (also with the settings in the original post) does.

I then proceeded to check what you mentioned and I tested in #318 (comment).

I then also saw that the Output channel in VS Code was using the CLI as seen in the original post and also shown once more in #318 (comment). That was not me using that, it was the VS Code output channel. It adds the --extend-select 'I001 as seen.

@charliermarsh
Copy link
Member

I think the confusion is perhaps that import sorting isn't part of ruff format -- it's part of ruff check (via ruff check --fix). In other words, import sorting is currently part of Ruff's linter, rather than Ruff's formatter, both for historical reasons and because it modifies the actual contents of the file, unlike the rest of the formatter, which is intended to keep the AST unchanged.

So if you run ruff format . locally, we won't resort your imports. We'll only reformat them, without moving them around, deduplicating them, merging them etc.

To run import sorting locally, you'd want to do (e.g.) ruff check --fix --select I .

If you're using VS Code, we're probably running Organize Imports on save, in addition to formatting?

@thernstig
Copy link
Author

thernstig commented Oct 30, 2023

Sorry, we both run ruff format . and ruff check . locally and none adds the line break for imports.

The important part is what I see in the output channel of VS Code. And when I try those commands in the CLI, it does add the line break, so those commands being run seems "wrong"?

@charliermarsh did you check the output I pasted from VS Code in my original post?

@charliermarsh
Copy link
Member

@thernstig - The original configuration you pasted above doesn't include I in the select. Can you confirm that you have I enabled in your pyproject.toml?

That output all looks correct to me. Most of those arguments aren't relevant for Ruff's diagnostic behavior and are instead required for LSP interoperability (e.g., we need Ruff to output JSON).

@thernstig
Copy link
Author

thernstig commented Oct 30, 2023

The original configuration I pasted in the first post is the config I have. Nothing else.

Are you saying this line, from the Ruff output panel, is actually never executed in VS Code on save?

2023-10-27 10:40:32.616 [info] Using interpreter executable: /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.617 [info] Found ruff 0.1.2 at /home/userA/code/foo/.venv/bin/ruff
2023-10-27 10:40:32.617 [info] Running Ruff with: /home/userA/code/foo/.venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--output-format', 'json', '-', '--fix', '--extend-ignore', 'ALL', '--extend-select', 'I001', '--stdin-filename', '/home/userA/code/foo/tests/utils/mongo.py']

@charliermarsh
Copy link
Member

That line is executed. That's from VS Code's Organize Imports command, which is either being run explicitly, or configured to run on on-save in your configuration. If you run Organize Imports in VS Code, and you have Ruff set to handle that command (or it's the only-installed extension that can handle it), we run Ruff with just that rule to do the import sorting.

I think you should either add I to your select in your pyproject.toml, or disable Ruff's import sorting in the VS Code extension. It just depends on what behavior you actually want. Do you want Ruff to organize your imports?

@thernstig
Copy link
Author

@charliermarsh then I think this is sorted, thank you for the patience. I understand why this happens then.

I think maybe "something" should be done?

The confusion might be a docs issue, possibly in https://github.com/astral-sh/ruff-vscode, as that mentions "source.organizeImports": true? Or should Organize Imports respect pyproject.toml so if I001 is not set there, it does nothing? That might of course generate other issues for you in the future. A possible solution would be to add a Organize Imports (forced) to not care about pyproject.toml, but maybe that is even more confusing. I am brainstorming here.

The confusion stems from that I thought that even an explicit Organize Imports command (or having set the config below), would still respect pyproject.toml and Ruff settings.

    "editor.codeActionsOnSave": {
      "source.organizeImports": true
    }

My solution for now is to add the I set of rules to my pyproject.toml then (or at least I001).

Or it might be entirely possibly you believe no change should be made at all to this, and it is my mistake on assuming that the pyproject.toml was always respected (even while using Organize Imports in VS Code).

@charliermarsh
Copy link
Member

Thanks -- I went ahead and added a note to the docs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Asking for support or clarification
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants