koxudaxi/ruff-pycharm-plugin

Gets confused when multiple projects are open

Kaelten opened this issue · 11 comments

When multiple projects are open, behaviors become confused.
I had two projects open:

  • A python 3.11 project that was developed against ruff.
  • A python 3.10 project that was developed against yapf.

I was migrating the 3.10 project over to ruff and had both projects open. During this, I kep having the ruff extension rewriting code to be 3.11 compatible. This was things like replacing references of datetime.timezone.UTC with datetime.UTC.

Ruff configuration in use between the two projects was near-identiical, one of the few exceptions was the target python version was set to py311 and py310 respectively.

This was driving me crazy. I disabled the extension to confirm that it was the ruff extension. Then it occured to me to try closing the 3.11 project window. After closing the 3.11 project window the bad behavior stopped immediately.

Expected behavior
Project settings from one project shouldn't impact other projects.

Environments (please complete the following information):

  • IDE: PyCharm Profession 2023.3.3 Build #PY-233.13763.11
  • OS: macOS 14.2.1
  • Ruff Version 0.2.1
  • Plugin version 0.0.29

Note: I've also noticed that sometimes one project detects the ruff executable from another project's .venv folder...

5j9 commented

Not sure if this is related or not, but I'm frequently getting the following error from pycharm when I have multiple projects open:

com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments: Read access is allowed from inside read-action (see Application.runReadAction()); see https://jb.gg/ij-platform-threading for details
Current thread: Thread[ApplicationImpl pooled thread 377,4,main] 1200124793 (EventQueue.isDispatchThread()=false)
SystemEventQueueThread: Thread[AWT-EventQueue-0,6,main] 298472496
	at com.intellij.util.concurrency.ThreadingAssertions.createThreadAccessException(ThreadingAssertions.java:149)
	at com.intellij.util.concurrency.ThreadingAssertions.softAssertReadAccess(ThreadingAssertions.java:107)
	at com.intellij.openapi.application.impl.ApplicationImpl.assertReadAccessAllowed(ApplicationImpl.java:1012)
	at com.intellij.openapi.fileEditor.FileDocumentManager.getDocument(FileDocumentManager.java:60)
	at com.intellij.psi.AbstractFileViewProvider.getDocument(AbstractFileViewProvider.java:170)
	at com.intellij.psi.AbstractFileViewProvider$VirtualFileContent.getText(AbstractFileViewProvider.java:446)
	at com.intellij.psi.AbstractFileViewProvider.getContents(AbstractFileViewProvider.java:151)
	at com.intellij.psi.impl.source.PsiFileImpl.getText(PsiFileImpl.java:307)
	at com.koxudaxi.ruff.SourceFile$text$2.invoke(Ruff.kt:273)
	at com.koxudaxi.ruff.SourceFile$text$2.invoke(Ruff.kt:270)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at com.koxudaxi.ruff.SourceFile.getText(Ruff.kt:270)
	at com.koxudaxi.ruff.SourceFile.getAsStdin(Ruff.kt:284)
	at com.koxudaxi.ruff.RuffKt.generateCommandArgs(Ruff.kt:309)
	at com.koxudaxi.ruff.RuffKt.runRuff(Ruff.kt:294)
	at com.koxudaxi.ruff.RuffApplyService$apply$$inlined$runReadActionOnPooledThread$default$1$1.call(Ruff.kt:524)
	at com.intellij.openapi.application.impl.ApplicationImpl$3.call(ApplicationImpl.java:280)
	at com.intellij.util.concurrency.ContextCallable.call(ContextCallable.java:32)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at com.intellij.util.concurrency.ContextRunnable.run(ContextRunnable.java:27)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:702)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:699)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:699)
	at java.base/java.lang.Thread.run(Thread.java:840)

I can confirm the same misbehaviour (as documented by @Kaelten ) and the same errors (like @5j9 ) when multiple projects with different ruff settings are open.

encountering the same issue. some further details, which seems to be the root cause of the problem:

  • in project 1, open your .idea/ruff.xml. note the value of <option name="projectRuffExecutablePath" value="$PROJECT_DIR$/ve/bin/ruff" />
  • edit something, attempting to trigger the plugin to run
  • in project 2, open .idea/ruff.xml. note that the value will have changed to <option name="projectRuffExecutablePath" value="$PROJECT_DIR$/../project_1_dir/ve/bin/ruff" /> instead of what it should be: <option name="projectRuffExecutablePath" value="$PROJECT_DIR$/ve/bin/ruff" />

you can manually edit each file in each project and the plugin will start working again, but eventually the changes you make to fix it will be overridden again (don't know what action specifically triggers this though).

Observing exactly what @bmrobin has said. As a work around, I'm trying to switch to a globally pipx install ruff / ruff-lsp for now. Then at least I won't have PyCharm telling me the ruff.xml file is changing but I guess then I run the risk of using a different version of ruff locally compared to in CI 🤷.

@Kaelten
I apologize for the delayed verification.
I took some time today to investigate the issue.
Indeed, I can confirm that the paths of different projects are being incorrectly written into the configuration file.
Of course, this behavior is not correct, but I couldn't immediately identify the cause.
I will look into it again tomorrow.

@Kaelten I apologize for the delayed verification. I took some time today to investigate the issue. Indeed, I can confirm that the paths of different projects are being incorrectly written into the configuration file. Of course, this behavior is not correct, but I couldn't immediately identify the cause. I will look into it again tomorrow.

I'm glad to hear you've verified it happening. Obviously this can create significant issues that has me questioning the continued use of the plugin and even pycharm as a whole. I hope you can find the cause and fix this soon.

Presumably a generic File Watcher could be configured to run ruff format, e.g. in watcherTasks.xml:

    <TaskOptions isEnabled="true">
      <option name="arguments" value="format $FilePath$" />
      <option name="checkSyntaxErrors" value="true" />
      <option name="description" />
      <option name="exitCodeBehavior" value="ERROR" />
      <option name="fileExtension" value="py" />
      <option name="immediateSync" value="false" />
      <option name="name" value="ruff" />
      <option name="output" value="" />
      <option name="outputFilters">
        <array />
      </option>
      <option name="outputFromStdout" value="false" />
      <option name="program" value="$PyInterpreterDirectory$/ruff" />
      <option name="runOnExternalChanges" value="false" />
      <option name="scopeName" value="Project Files" />
      <option name="trackOnlyRoot" value="false" />
      <option name="workingDir" value="" />
      <envs />
    </TaskOptions>

Anyway, I think a lot of the newest issues here are actually just different ways of stating this bug, so it does seem to be affecting a lot of people:

#493
#464
#438
#416

Thank you for reporting it.
I found the root cause.

class RuffPackageManagerListener(project: Project) : PyPackageManager.Listener {
private val ruffConfigService = RuffConfigService.getInstance(project)
private val ruffCacheService = RuffCacheService.getInstance(project)
private val ruffLspClientManager = RuffLspClientManager.getInstance(project)
override fun packagesRefreshed(sdk: Sdk) {
ruffConfigService.projectRuffExecutablePath = findRuffExecutableInSDK(sdk, false)?.absolutePath
ruffConfigService.projectRuffLspExecutablePath = findRuffExecutableInSDK(sdk, true)?.absolutePath

The SDK should be for the project.
But, it is not guaranteed. It means override other project configs.

I'm thinking of the solution.

@Kaelten @dpmccabe @5j9 @ceeeeej
I'm sorry for my late reply.
I published the fixed version as 0.0.39

@koxudaxi thank you much. This has indeed been fixed for me lately and it's making an incredible difference in my workdays. Thank you.