diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml
index 7f0195448b8a..d0aa4c0ff7ad 100644
--- a/python/pluginResources/intellij.python.community.impl.xml
+++ b/python/pluginResources/intellij.python.community.impl.xml
@@ -249,9 +249,11 @@
-
+ implementationClass="com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection"
+ groupKey="INSP.GROUP.pandas"/>
@@ -517,9 +519,6 @@
-
-
@@ -557,27 +556,44 @@
description="Use project location as a working directory for the packaging tool execution."/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
@@ -879,23 +897,28 @@
-
-
-
+
+
-
-
+
+
-
+
-
+
diff --git a/python/src/com/jetbrains/python/black/BlackFormatterAdvertiserService.kt b/python/src/com/jetbrains/python/black/BlackFormatterAdvertiserService.kt
index e2f37a1e4ae6..9c31fee54e44 100644
--- a/python/src/com/jetbrains/python/black/BlackFormatterAdvertiserService.kt
+++ b/python/src/com/jetbrains/python/black/BlackFormatterAdvertiserService.kt
@@ -16,6 +16,7 @@ import com.jetbrains.python.PyBundle
import com.jetbrains.python.black.configuration.BlackFormatterConfigurable
import com.jetbrains.python.black.configuration.BlackFormatterConfiguration
import com.jetbrains.python.sdk.pythonSdk
+import com.jetbrains.python.statistics.BlackFormatterIntegrationIdsHolder.Companion.BLACK_FORMATTER_SUPPORT
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
@@ -69,7 +70,7 @@ class BlackFormatterAdvertiserService private constructor() {
val notification = NotificationGroupManager.getInstance().getNotificationGroup(BlackFormattingService.NOTIFICATION_GROUP_ID)
.createNotification(PyBundle.message("black.advertising.service.notification.title"), message, NotificationType.INFORMATION)
- .setDisplayId("black.formatter")
+ .setDisplayId(BLACK_FORMATTER_SUPPORT)
.setSuggestionType(true)
.setImportantSuggestion(true)
.addAction(NotificationAction
diff --git a/python/src/com/jetbrains/python/debugger/PyCythonExtensionWarning.java b/python/src/com/jetbrains/python/debugger/PyCythonExtensionWarning.java
index 12c5f95e4e75..ae643143c8db 100644
--- a/python/src/com/jetbrains/python/debugger/PyCythonExtensionWarning.java
+++ b/python/src/com/jetbrains/python/debugger/PyCythonExtensionWarning.java
@@ -42,6 +42,8 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
+import static com.jetbrains.python.statistics.CythonWarningIdsHolder.CYTHON_EXTENSION_SPEEDS_UP_PYTHON_DEBUGGING;
+
public final class PyCythonExtensionWarning {
private static final Logger LOG = Logger.getInstance(PyCythonExtensionWarning.class);
@@ -56,7 +58,7 @@ public final class PyCythonExtensionWarning {
Notification notification =
new Notification(CYTHON_WARNING_GROUP_ID, PyBundle.message("compile.cython.extensions.notification"),
PyBundle.message("debugger.cython.extension.speeds.up.python.debugging"),
- NotificationType.INFORMATION);
+ NotificationType.INFORMATION).setDisplayId(CYTHON_EXTENSION_SPEEDS_UP_PYTHON_DEBUGGING);
notification.setSuggestionType(true);
notification.setListener(NotificationListener.URL_OPENING_LISTENER);
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
index c28f8041fd1b..3588d604c5b0 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
@@ -86,6 +86,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static com.jetbrains.python.debugger.variablesview.usertyperenderers.ConfigureTypeRenderersActionKt.getTypeRenderer;
import static com.jetbrains.python.debugger.variablesview.usertyperenderers.ConfigureTypeRenderersActionKt.loadTypeRendererChildren;
+import static com.jetbrains.python.statistics.PythonDebuggerIdsHolder.CONNECTION_FAILED;
public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
@@ -366,6 +367,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
if (shouldLogConnectionException(e)) {
getNotificationGroup()
.createNotification(PyBundle.message("debug.notification.title.connection.failed"), e.getMessage(), NotificationType.ERROR)
+ .setDisplayId(CONNECTION_FAILED)
.notify(myProject);
}
}
diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspectionAdvertiser.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspectionAdvertiser.java
index c9cec907aff9..beef704ba049 100644
--- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspectionAdvertiser.java
+++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspectionAdvertiser.java
@@ -39,6 +39,8 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import static com.jetbrains.python.statistics.PythonCompatibilityInspectionAdvertiserIdsHolder.*;
+
/**
* @author Mikhail Golubev
*/
@@ -79,11 +81,11 @@ public final class PyCompatibilityInspectionAdvertiser implements Annotator {
}
else if (containsFutureImports(pyFile)) {
showSingletonNotification(
- project, PyBundle.message("python.compatibility.inspection.advertiser.using.future.imports.warning.message"));
+ project, PyBundle.message("python.compatibility.inspection.advertiser.using.future.imports.warning.message"), USING_FUTURE_IMPORTS);
}
else if (PyPsiUtils.containsImport(pyFile, "six")) {
showSingletonNotification(
- project, PyBundle.message("python.compatibility.inspection.advertiser.using.six.warning.message"));
+ project, PyBundle.message("python.compatibility.inspection.advertiser.using.six.warning.message"), USING_SIX_PACKAGE);
}
}
}
@@ -115,6 +117,7 @@ public final class PyCompatibilityInspectionAdvertiser implements Annotator {
project,
PyBundle.message("python.compatibility.inspection.advertiser.notifications.title"),
message,
+ STALE_PYTHON_VERSION,
(notification, event) -> {
final boolean enabled = "#yes".equals(event.getDescription());
if (enabled) {
@@ -153,11 +156,12 @@ public final class PyCompatibilityInspectionAdvertiser implements Annotator {
}
}
- private static void showSingletonNotification(@NotNull Project project, @NotificationContent String msg) {
+ private static void showSingletonNotification(@NotNull Project project, @NotificationContent String msg, @NotNull String displayId) {
showSingletonNotification(
project,
PyBundle.message("python.compatibility.inspection.advertiser.notifications.title"),
msg,
+ displayId,
(notification, event) -> {
final boolean enabled = "#yes".equals(event.getDescription());
if (enabled) {
@@ -172,10 +176,12 @@ public final class PyCompatibilityInspectionAdvertiser implements Annotator {
private static void showSingletonNotification(@NotNull Project project,
@NotNull @NotificationTitle String title,
@NotNull @NotificationContent String htmlContent,
+ @NotNull String displayId,
@NotNull NotificationListener listener) {
project.putUserData(DONT_SHOW_BALLOON, true);
NotificationGroupManager.getInstance().getNotificationGroup("Python Compatibility Inspection Advertiser")
.createNotification(title, htmlContent, NotificationType.INFORMATION)
+ .setDisplayId(displayId)
.setSuggestionType(true)
.setListener((notification, event) -> {
try {
@@ -214,7 +220,8 @@ public final class PyCompatibilityInspectionAdvertiser implements Annotator {
private static @Nullable LanguageLevel getLatestConfiguredCompatiblePythonVersion(@NotNull PsiElement anchor) {
final InspectionProfile profile = InspectionProfileManager.getInstance(anchor.getProject()).getCurrentProfile();
- final PyCompatibilityInspection inspection = (PyCompatibilityInspection)profile.getUnwrappedTool(getCompatibilityInspectionShortName(), anchor);
+ final PyCompatibilityInspection inspection =
+ (PyCompatibilityInspection)profile.getUnwrappedTool(getCompatibilityInspectionShortName(), anchor);
assert inspection != null;
final JDOMExternalizableStringList versions = inspection.ourVersions;
if (versions.isEmpty()) {
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/IgnoreRequirementFix.kt b/python/src/com/jetbrains/python/inspections/quickfix/IgnoreRequirementFix.kt
index 96d2f6088a75..db05925a3f77 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/IgnoreRequirementFix.kt
+++ b/python/src/com/jetbrains/python/inspections/quickfix/IgnoreRequirementFix.kt
@@ -16,6 +16,7 @@ import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.inspections.PyPackageRequirementsInspection
+import com.jetbrains.python.statistics.PackageRequirementsIdsHolder.Companion.REQUIREMENTS_HAVE_BEEN_IGNORED
import org.jetbrains.annotations.Nls
internal class IgnoreRequirementFix(private val packagesToIgnore: Set) : LocalQuickFix {
@@ -38,7 +39,9 @@ internal class IgnoreRequirementFix(private val packagesToIgnore: Set) :
else -> PyPsiBundle.message("INSP.package.requirements.requirements.have.been.ignored")
}
- val notification = BALLOON_NOTIFICATIONS.createNotification(notificationMessage, NotificationType.INFORMATION)
+ val notification = BALLOON_NOTIFICATIONS
+ .createNotification(notificationMessage, NotificationType.INFORMATION)
+ .setDisplayId(REQUIREMENTS_HAVE_BEEN_IGNORED)
notification.addAction(createUndoAction(inspection, packagesToIgnore, profileManager))
notification.addAction(createEditSettingsAction(project))
notification.notify(project)
diff --git a/python/src/com/jetbrains/python/packaging/PyRequirementsTxtUtil.kt b/python/src/com/jetbrains/python/packaging/PyRequirementsTxtUtil.kt
index 151e718d6cde..d923e5ba5d26 100644
--- a/python/src/com/jetbrains/python/packaging/PyRequirementsTxtUtil.kt
+++ b/python/src/com/jetbrains/python/packaging/PyRequirementsTxtUtil.kt
@@ -36,6 +36,10 @@ import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.sdk.PySdkPopupFactory
import com.jetbrains.python.sdk.PythonSdkAdditionalData
import com.jetbrains.python.sdk.PythonSdkUtil
+import com.jetbrains.python.statistics.SyncPythonRequirementsIdsHolder.Companion.ANALYZE_ENTRIES_IN_REQUIREMENTS_FILE_FAILED
+import com.jetbrains.python.statistics.SyncPythonRequirementsIdsHolder.Companion.CREATE_REQUIREMENTS_FILE_FAILED
+import com.jetbrains.python.statistics.SyncPythonRequirementsIdsHolder.Companion.NO_INTERPRETER_CONFIGURED
+import com.jetbrains.python.statistics.SyncPythonRequirementsIdsHolder.Companion.SOME_REQUIREMENTS_FROM_BASE_FILES_WERE_NOT_UPDATED
import com.jetbrains.python.util.runWithModalBlockingOrInBackground
import org.jetbrains.annotations.ApiStatus
import java.util.*
@@ -101,11 +105,14 @@ internal fun syncWithImports(module: Module) {
val configureSdkAction = NotificationAction.createSimpleExpiring(PySdkBundle.message("python.configure.interpreter.action")) {
PySdkPopupFactory.createAndShow(module)
}
- showNotification(notificationGroup,
- NotificationType.ERROR,
- PyBundle.message("python.requirements.error.no.interpreter"),
- module.project,
- configureSdkAction)
+ showNotification(
+ notificationGroup = notificationGroup,
+ type = NotificationType.ERROR,
+ text = PyBundle.message("python.requirements.error.no.interpreter"),
+ project = module.project,
+ action = configureSdkAction,
+ displayId = NO_INTERPRETER_CONFIGURED
+ )
return
}
val settings = PyPackageRequirementsSettings.getInstance(module)
@@ -121,7 +128,13 @@ internal fun syncWithImports(module: Module) {
if (requirementsFile == null) {
val text = PyBundle.message("python.requirements.error.create.requirements.file")
- showNotification(notificationGroup, NotificationType.WARNING, text, module.project)
+ showNotification(
+ notificationGroup = notificationGroup,
+ type = NotificationType.WARNING,
+ text = text,
+ project = module.project,
+ displayId = CREATE_REQUIREMENTS_FILE_FAILED
+ )
return
}
@@ -139,11 +152,23 @@ internal fun syncWithImports(module: Module) {
if (matchResult.unhandledLines.isNotEmpty()) {
val text = PyBundle.message("python.requirements.warning.unhandled.lines", matchResult.unhandledLines.joinToString(", "))
- showNotification(notificationGroup, NotificationType.WARNING, text, module.project)
+ showNotification(
+ notificationGroup = notificationGroup,
+ type = NotificationType.WARNING,
+ text = text,
+ project = module.project,
+ displayId = ANALYZE_ENTRIES_IN_REQUIREMENTS_FILE_FAILED
+ )
}
if (matchResult.unchangedInBaseFiles.isNotEmpty()) {
val text = PyBundle.message("python.requirements.info.file.ref.dropped", matchResult.unchangedInBaseFiles.joinToString(", "))
- showNotification(notificationGroup, NotificationType.INFORMATION, text, module.project)
+ showNotification(
+ notificationGroup = notificationGroup,
+ type = NotificationType.INFORMATION,
+ text = text,
+ project = module.project,
+ displayId = SOME_REQUIREMENTS_FROM_BASE_FILES_WERE_NOT_UPDATED
+ )
}
}
@@ -153,8 +178,9 @@ private fun showNotification(
@NlsContexts.NotificationContent text: String,
project: Project,
action: NotificationAction? = null,
+ displayId: String,
) {
- val notification = notificationGroup.createNotification(PyBundle.message("python.requirements.balloon"), text, type)
+ val notification = notificationGroup.createNotification(PyBundle.message("python.requirements.balloon"), text, type).setDisplayId(displayId)
if (action != null) notification.addAction(action)
notification.notify(project)
}
diff --git a/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt b/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt
index 07029ec3854d..7efeba5c5833 100644
--- a/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt
+++ b/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt
@@ -37,6 +37,8 @@ import com.jetbrains.python.packaging.statistics.PythonPackagesToolwindowStatist
import com.jetbrains.python.packaging.toolwindow.model.*
import com.jetbrains.python.sdk.PythonSdkUtil
import com.jetbrains.python.sdk.pythonSdk
+import com.jetbrains.python.statistics.PythonPackagesIdsHolder.Companion.PYTHON_PACKAGE_DELETED
+import com.jetbrains.python.statistics.PythonPackagesIdsHolder.Companion.PYTHON_PACKAGE_INSTALLED
import kotlinx.coroutines.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
@@ -192,7 +194,10 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
suspend fun installPackage(installRequest: PythonPackageInstallRequest, options: List = emptyList()) {
PythonPackagesToolwindowStatisticsCollector.installPackageEvent.log(project)
managerUI.installPackagesRequestBackground(installRequest, options)?.let {
- handleActionCompleted(message("python.packaging.notification.installed", installRequest.title))
+ handleActionCompleted(
+ text = message("python.packaging.notification.installed", installRequest.title),
+ displayId = PYTHON_PACKAGE_INSTALLED
+ )
}
}
@@ -200,14 +205,20 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
val installRequest = manager?.findPackageSpecification(pkg.name, pkg.version)?.toInstallRequest() ?: return
PythonPackagesToolwindowStatisticsCollector.installPackageEvent.log(project)
managerUI.installPackagesRequestBackground(installRequest, options)?.let {
- handleActionCompleted(message("python.packaging.notification.installed", installRequest.title))
+ handleActionCompleted(
+ text = message("python.packaging.notification.installed", installRequest.title),
+ displayId = PYTHON_PACKAGE_INSTALLED
+ )
}
}
suspend fun deletePackage(vararg selectedPackages: InstalledPackage) {
PythonPackagesToolwindowStatisticsCollector.uninstallPackageEvent.log(project)
managerUI.uninstallPackagesBackground(selectedPackages.map { it.instance.name }) ?: return
- handleActionCompleted(message("python.packaging.notification.deleted", selectedPackages.joinToString(", ") { it.name }))
+ handleActionCompleted(
+ text = message("python.packaging.notification.deleted", selectedPackages.joinToString(", ") { it.name }),
+ displayId = PYTHON_PACKAGE_DELETED
+ )
}
@ApiStatus.Internal
@@ -379,15 +390,16 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
}
}
- private suspend fun handleActionCompleted(text: @Nls String) {
+ private suspend fun handleActionCompleted(text: @Nls String, displayId: String) {
VirtualFileManager.getInstance().asyncRefresh()
- showPackagingNotification(text)
+ showPackagingNotification(text, displayId)
}
- private suspend fun showPackagingNotification(text: @Nls String) {
+ private suspend fun showPackagingNotification(text: @Nls String, displayId: String) {
val notification = NotificationGroupManager.getInstance()
.getNotificationGroup("PythonPackages")
.createNotification(text, NotificationType.INFORMATION)
+ .setDisplayId(displayId)
withContext(Dispatchers.Main) {
notification.notify(project)
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
index 88148fbd8b75..0df4d9b7885a 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
@@ -62,6 +62,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static com.intellij.execution.target.TargetBasedSdks.loadTargetConfiguration;
+import static com.jetbrains.python.statistics.PythonSDKUpdaterIdsHolder.REFRESH_SKELETONS_FOR_REMOTE_INTERPRETER_FAILED;
/**
* Class should be final and singleton since some code checks its instance by ref.
@@ -384,7 +385,7 @@ public final class PythonSdkType extends SdkType {
Notification notification =
new Notification("Python SDK Updater", PyBundle.message("sdk.gen.failed.notification.title"), notificationMessage,
- NotificationType.WARNING);
+ NotificationType.WARNING).setDisplayId(REFRESH_SKELETONS_FOR_REMOTE_INTERPRETER_FAILED);
if (notificationListener != null) notification.setListener(notificationListener);
notification.notify(null);
}
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
index cefc8942b067..92b69faecd3f 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
@@ -64,6 +64,9 @@ import java.util.*;
import java.util.List;
import java.util.function.Function;
+import static com.jetbrains.python.statistics.PythonSDKUpdaterIdsHolder.REFRESH_SKELETONS_FOR_REMOTE_INTERPRETER_FAILED;
+import static com.jetbrains.python.statistics.PythonSDKUpdaterIdsHolder.REMOTE_INTERPRETER_SUPPORT_IS_NOT_AVAILABLE;
+
/**
* Refreshes all project's Python SDKs.
*/
@@ -403,6 +406,7 @@ public final class PythonSdkUpdater {
.createNotification(PyBundle.message("sdk.gen.failed.notification.title"),
PyBundle.message("remote.interpreter.support.is.not.available", sdk.getName()),
NotificationType.WARNING)
+ .setDisplayId(REMOTE_INTERPRETER_SUPPORT_IS_NOT_AVAILABLE)
.notify(myProject);
}
else if (exception instanceof InvalidSdkException) {
diff --git a/python/src/com/jetbrains/python/sdk/add/v2/common.kt b/python/src/com/jetbrains/python/sdk/add/v2/common.kt
index f1aa28b76378..6e91e7d143ab 100644
--- a/python/src/com/jetbrains/python/sdk/add/v2/common.kt
+++ b/python/src/com/jetbrains/python/sdk/add/v2/common.kt
@@ -37,6 +37,7 @@ import com.jetbrains.python.sdk.pipenv.PIPENV_ICON
import com.jetbrains.python.sdk.poetry.POETRY_ICON
import com.jetbrains.python.sdk.uv.UV_ICON
import com.jetbrains.python.statistics.InterpreterTarget
+import com.jetbrains.python.statistics.PythonInterpreterInstallationIdsHolder.Companion.PYTHON_INSTALLATION_INTERRUPTED
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.annotations.ApiStatus
import javax.swing.Icon
@@ -157,6 +158,7 @@ internal fun installBaseSdk(sdk: Sdk, existingSdks: List): Sdk? {
val notification = NotificationGroupManager.getInstance()
.getNotificationGroup("Python interpreter installation")
.createNotification(message("python.sdk.installation.balloon.error.message"), NotificationType.ERROR)
+ .setDisplayId(PYTHON_INSTALLATION_INTERRUPTED)
notification.collapseDirection
notification.addAction(NotificationAction.createSimple(message("python.sdk.installation.balloon.error.action")) {
diff --git a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt
index 8fb10a6e8143..d9432f99d44f 100644
--- a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt
+++ b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt
@@ -28,6 +28,7 @@ import com.jetbrains.python.sdk.configuration.suppressors.PyPackageRequirementsI
import com.jetbrains.python.sdk.configuration.suppressors.TipOfTheDaySuppressor
import com.jetbrains.python.sdk.configurePythonSdk
import com.jetbrains.python.sdk.uv.isUv
+import com.jetbrains.python.statistics.ConfiguredPythonInterpreterIdsHolder.Companion.SDK_HAS_BEEN_CONFIGURED_AS_THE_PROJECT_INTERPRETER
import com.jetbrains.python.util.ShowingMessageErrorSync
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -108,16 +109,19 @@ object PyProjectSdkConfiguration {
private fun notifyAboutConfiguredSdk(project: Project, module: Module, sdk: Sdk) {
if (isNotificationSilentMode(project)) return
- NotificationGroupManager.getInstance().getNotificationGroup("ConfiguredPythonInterpreter").createNotification(
- PyBundle.message("sdk.has.been.configured.as.the.project.interpreter", sdk.name),
- NotificationType.INFORMATION
- ).apply {
- val configureSdkAction = NotificationAction.createSimpleExpiring(PySdkBundle.message("python.configure.interpreter.action")) {
- PySdkPopupFactory.createAndShow(module)
- }
+ NotificationGroupManager.getInstance().getNotificationGroup("ConfiguredPythonInterpreter")
+ .createNotification(
+ content = PyBundle.message("sdk.has.been.configured.as.the.project.interpreter", sdk.name),
+ type = NotificationType.INFORMATION
+ )
+ .setDisplayId(SDK_HAS_BEEN_CONFIGURED_AS_THE_PROJECT_INTERPRETER)
+ .apply {
+ val configureSdkAction = NotificationAction.createSimpleExpiring(PySdkBundle.message("python.configure.interpreter.action")) {
+ PySdkPopupFactory.createAndShow(module)
+ }
- addAction(configureSdkAction)
- notify(project)
- }
+ addAction(configureSdkAction)
+ notify(project)
+ }
}
}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt b/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt
index 68ce014e1fab..6b16ea607eee 100644
--- a/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt
+++ b/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt
@@ -25,6 +25,7 @@ import com.jetbrains.python.PyBundle
import com.jetbrains.python.onFailure
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
import com.jetbrains.python.sdk.*
+import com.jetbrains.python.statistics.PipfileWatcherIdsHolder.Companion.RUN_PIPENV_LOCK_SUGGESTION
import com.jetbrains.python.util.ShowingMessageErrorSync
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -77,7 +78,11 @@ internal class PipEnvPipFileWatcher : EditorFactoryListener {
}
val content = PyBundle.message("python.sdk.pipenv.pip.file.notification.content")
val notification = withContext(Dispatchers.EDT) {
- LOCK_NOTIFICATION_GROUP.createNotification(title, content, NotificationType.INFORMATION)
+ LOCK_NOTIFICATION_GROUP.createNotification(
+ title = title,
+ content = content,
+ type = NotificationType.INFORMATION,
+ ).setDisplayId(RUN_PIPENV_LOCK_SUGGESTION)
}
.setListener(NotificationListener { notification, event ->
notification.expire()
diff --git a/python/src/com/jetbrains/python/statistics/notificationIdHolders.kt b/python/src/com/jetbrains/python/statistics/notificationIdHolders.kt
new file mode 100644
index 000000000000..fd6a0671202b
--- /dev/null
+++ b/python/src/com/jetbrains/python/statistics/notificationIdHolders.kt
@@ -0,0 +1,138 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.jetbrains.python.statistics
+
+import com.intellij.notification.impl.NotificationIdsHolder
+import org.jetbrains.annotations.ApiStatus
+
+internal class ConfiguredPythonInterpreterIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ SDK_HAS_BEEN_CONFIGURED_AS_THE_PROJECT_INTERPRETER,
+ )
+
+ companion object {
+ const val SDK_HAS_BEEN_CONFIGURED_AS_THE_PROJECT_INTERPRETER = "sdk.has.been.configured.as.the.project.interpreter"
+ }
+}
+
+internal class PythonPackagesIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ PYTHON_PACKAGE_INSTALLED,
+ PYTHON_PACKAGE_DELETED,
+ )
+
+ companion object {
+ const val PYTHON_PACKAGE_INSTALLED = "python.package.installed"
+ const val PYTHON_PACKAGE_DELETED = "python.package.deleted"
+ }
+}
+
+@ApiStatus.Internal
+class PythonDebuggerIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ CONNECTION_FAILED,
+ JUPYTER_CELL_EDITED_BETWEEN_SESSIONS,
+ JUPYTER_CELL_EDITED_DURING_SESSIONS,
+ JUPYTER_OTHER_CELL_UNDER_DEBUGGER,
+ )
+
+ companion object {
+ const val CONNECTION_FAILED: String = "connection.failed"
+ const val JUPYTER_CELL_EDITED_BETWEEN_SESSIONS: String = "jupyter.cell.edited.between.sessions"
+ const val JUPYTER_CELL_EDITED_DURING_SESSIONS: String = "jupyter.cell.edited.during.sessions"
+ const val JUPYTER_OTHER_CELL_UNDER_DEBUGGER: String = "jupyter.other.cell.under.debugger"
+ }
+}
+
+
+internal class PythonCompatibilityInspectionAdvertiserIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ STALE_PYTHON_VERSION,
+ USING_FUTURE_IMPORTS,
+ USING_SIX_PACKAGE,
+ )
+
+ companion object {
+ const val STALE_PYTHON_VERSION = "stale.python.version"
+ const val USING_FUTURE_IMPORTS = "using.future.imports"
+ const val USING_SIX_PACKAGE = "using.six.package"
+ }
+}
+
+
+internal class CythonWarningIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ CYTHON_EXTENSION_SPEEDS_UP_PYTHON_DEBUGGING,
+ )
+
+ companion object {
+ const val CYTHON_EXTENSION_SPEEDS_UP_PYTHON_DEBUGGING = "cython.extension.speeds.up.python.debugging"
+ }
+}
+
+internal class PackageRequirementsIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ REQUIREMENTS_HAVE_BEEN_IGNORED,
+ )
+
+ companion object {
+ const val REQUIREMENTS_HAVE_BEEN_IGNORED = "requirements.have.been.ignored"
+ }
+}
+
+internal class BlackFormatterIntegrationIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ BLACK_FORMATTER_SUPPORT,
+ )
+
+ companion object {
+ const val BLACK_FORMATTER_SUPPORT = "black.formatter.support"
+ }
+}
+
+internal class PythonSDKUpdaterIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ REFRESH_SKELETONS_FOR_REMOTE_INTERPRETER_FAILED,
+ REMOTE_INTERPRETER_SUPPORT_IS_NOT_AVAILABLE,
+ )
+
+ companion object {
+ const val REFRESH_SKELETONS_FOR_REMOTE_INTERPRETER_FAILED = "refresh.skeletons.for.remote.interpreter.failed"
+ const val REMOTE_INTERPRETER_SUPPORT_IS_NOT_AVAILABLE = "remote.interpreter.support.is.not.available"
+ }
+}
+
+internal class SyncPythonRequirementsIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ NO_INTERPRETER_CONFIGURED,
+ CREATE_REQUIREMENTS_FILE_FAILED,
+ ANALYZE_ENTRIES_IN_REQUIREMENTS_FILE_FAILED,
+ SOME_REQUIREMENTS_FROM_BASE_FILES_WERE_NOT_UPDATED,
+ )
+
+ companion object {
+ const val NO_INTERPRETER_CONFIGURED = "no.interpreter.configured"
+ const val CREATE_REQUIREMENTS_FILE_FAILED = "create.requirements.file.failed"
+ const val ANALYZE_ENTRIES_IN_REQUIREMENTS_FILE_FAILED = "analyze.entries.in.requirements.file.failed"
+ const val SOME_REQUIREMENTS_FROM_BASE_FILES_WERE_NOT_UPDATED = "some.requirements.from.base.files.were.not.updated"
+ }
+}
+
+internal class PythonInterpreterInstallationIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ PYTHON_INSTALLATION_INTERRUPTED,
+ )
+
+ companion object {
+ const val PYTHON_INSTALLATION_INTERRUPTED = "python.installation.interrupted"
+ }
+}
+
+internal class PipfileWatcherIdsHolder : NotificationIdsHolder {
+ override fun getNotificationIds(): List = listOf(
+ RUN_PIPENV_LOCK_SUGGESTION,
+ )
+
+ companion object {
+ const val RUN_PIPENV_LOCK_SUGGESTION = "run.pipenv.lock.suggestion"
+ }
+}