[python] add display ids for notifications (PY-83036)

Merge-request: IJ-MR-171306
Merged-by: Vitaly Legchilkin <Vitaly.Legchilkin@jetbrains.com>

GitOrigin-RevId: 7a4b12f4bee6fae93ce5351bda0e19289b8f7404
This commit is contained in:
Vitaly Legchilkin
2025-08-05 08:56:42 +00:00
committed by intellij-monorepo-bot
parent 2eb3e69abb
commit cc03cdfb94
14 changed files with 279 additions and 49 deletions
@@ -249,9 +249,11 @@
<localInspection language="Python" shortName="PyPandasSeriesToListInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
key="INSP.pandas.series.values.replace.with.tolist" enabledByDefault="true" level="WARNING"
implementationClass="com.jetbrains.python.inspections.PyPandasSeriesToListInspection" groupKey="INSP.GROUP.pandas"/>
<localInspection language="Python" shortName="PyPandasTruthValueIsAmbiguousInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
<localInspection language="Python" shortName="PyPandasTruthValueIsAmbiguousInspection" suppressId="PyPackages"
bundle="messages.PyPsiBundle"
key="INSP.pandas.truth.value.is.ambiguous.df" enabledByDefault="true" level="WARNING"
implementationClass="com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection" groupKey="INSP.GROUP.pandas"/>
implementationClass="com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection"
groupKey="INSP.GROUP.pandas"/>
<listSplitJoinContext language="Python" implementationClass="com.jetbrains.python.codeInsight.editorActions.PyListSplitJoinContext"/>
@@ -517,9 +519,6 @@
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.debugger.statistics.PyDataViewerCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.python.community.impl.installer.BinaryInstallerUsagesCollector"/>
<notificationGroup id="Python Stub Packages Advertiser" displayType="STICKY_BALLOON" bundle="messages.PyBundle"
key="code.insight.stub.package.advertiser.notifications.group.title"/>
<!-- Code-insight IDE bridge -->
<applicationService serviceInterface="com.jetbrains.python.PythonRuntimeService"
serviceImplementation="com.jetbrains.python.PythonRuntimeServiceImpl"/>
@@ -557,27 +556,44 @@
description="Use project location as a working directory for the packaging tool execution."/>
<registryKey key="python.packaging.tool.upload.project" defaultValue="false"
description="Upload project files and use this location as a working directory for the packaging tool execution."/>
<notificationGroup id="Python Debugger" displayType="TOOL_WINDOW" toolWindowId="Debug" bundle="messages.PyBundle"
key="debug.notification.group"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PythonDebuggerIdsHolder"/>
<notificationGroup id="Python SDK Updater" displayType="BALLOON" bundle="messages.PyBundle"
key="python.sdk.updater.notifications.group.title"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PythonSDKUpdaterIdsHolder"/>
<notificationGroup id="Sync Python requirements" displayType="BALLOON" bundle="messages.PyBundle" key="python.requirements.balloon"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.SyncPythonRequirementsIdsHolder"/>
<notificationGroup id="CythonWarning" displayType="BALLOON" bundle="messages.PyBundle" key="notification.group.cython"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.CythonWarningIdsHolder"/>
<notificationGroup id="Python Compatibility Inspection Advertiser" displayType="STICKY_BALLOON" isLogByDefault="false"
bundle="messages.PyBundle" key="python.compatibility.inspection.advertiser.notifications.group.title"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PythonCompatibilityInspectionAdvertiserIdsHolder"/>
<notificationGroup id="Package requirements" displayType="BALLOON" isLogByDefault="false" bundle="messages.PyBundle"
key="notification.group.package.requirements"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PackageRequirementsIdsHolder"/>
<notificationGroup id="Pipfile Watcher" displayType="STICKY_BALLOON" isLogByDefault="false" bundle="messages.PyBundle"
key="python.sdk.pipenv.pip.file.watcher"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PipfileWatcherIdsHolder"/>
<notificationGroup id="ConfiguredPythonInterpreter" displayType="BALLOON" isLogByDefault="true" bundle="messages.PyBundle"
key="sdk.has.been.configured.notification.name"/>
<notificationGroup id="Packaging" displayType="BALLOON" bundle="messages.PyBundle" key="notification.group.packaging"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.ConfiguredPythonInterpreterIdsHolder"/>
<notificationGroup id="PythonPackages" displayType="TOOL_WINDOW" toolWindowId="Python Packages"
bundle="messages.PyBundle" key="notification.group.packaging.toolwindow"/>
<notificationGroup id="pyproject.toml Watcher" displayType="STICKY_BALLOON" isLogByDefault="true" bundle="messages.PyBundle"
key="python.sdk.poetry.pip.file.watcher"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PythonPackagesIdsHolder"/>
<notificationGroup id="Python interpreter installation" displayType="STICKY_BALLOON" isLogByDefault="true" bundle="messages.PyBundle"
key="python.sdk.installation.notification.group"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.PythonInterpreterInstallationIdsHolder"/>
<toolWindow id="Python Packages" anchor="bottom"
icon="com.jetbrains.python.icons.PythonIcons.Python.PythonPackages"
@@ -623,6 +639,8 @@
<notificationGroup id="Black Formatter Integration" displayType="STICKY_BALLOON" isLogByDefault="true" bundle="messages.PyBundle"
key="black.configurable.name"/>
<statistics.notificationIdsHolder implementation="com.jetbrains.python.statistics.BlackFormatterIntegrationIdsHolder"/>
<dependencyCollector kind="python" implementation="com.jetbrains.python.packaging.PyDependencyCollector"/>
<!-- Parameter Info -->
@@ -879,23 +897,28 @@
<PythonPackageRequiresExtractorProvider implementation="com.jetbrains.python.poetry.packaging.PoetryRequiresExtractorProvider"/>
<PythonPackageRequiresExtractorProvider implementation="com.jetbrains.python.uv.packaging.UvPackageRequiresExtractorProvider"/>
<PythonPackageRequirementsTreeExtractorProvider implementation="com.jetbrains.python.poetry.packaging.PoetryPackageRequirementsTreeExtractorProvider"/>
<PythonPackageRequirementsTreeExtractorProvider implementation="com.jetbrains.python.uv.packaging.UvPackageRequirementsTreeExtractorProvider"/>
<PythonPackageRequirementsTreeExtractorProvider
implementation="com.jetbrains.python.poetry.packaging.PoetryPackageRequirementsTreeExtractorProvider"/>
<PythonPackageRequirementsTreeExtractorProvider
implementation="com.jetbrains.python.uv.packaging.UvPackageRequirementsTreeExtractorProvider"/>
<systemPythonProvider implementation="com.jetbrains.python.sdk.uv.UvSystemPythonProvider"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.LegacySystemPythonProvider"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.BrewSystemPythonProvider" os="mac"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.MacSystemPythonProvider" os="mac"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.BrewSystemPythonProvider"
os="mac"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.MacSystemPythonProvider"
os="mac"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.WindowsSystemPythonProvider" os="windows"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.WindowsSystemPythonProvider"
os="windows"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.AsdfSystemPythonProvider"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.PyenvSystemPythonProvider"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.UnixSystemPythonProvider" os="unix"/>
<systemPythonProvider implementation="com.intellij.python.community.services.systemPython.impl.providers.UnixSystemPythonProvider"
os="unix"/>
<inspectionExtension implementation="com.jetbrains.python.sdk.configuration.suppressors.PyInterpreterInspectionSuppressor"/>
<inspectionExtension implementation="com.jetbrains.python.inspections.PyUnresolvedReferenceDefaultInspectionExtension" order="last"/>
@@ -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
@@ -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);
@@ -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);
}
}
@@ -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()) {
@@ -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<String>) : LocalQuickFix {
@@ -38,7 +39,9 @@ internal class IgnoreRequirementFix(private val packagesToIgnore: Set<String>) :
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)
@@ -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)
}
@@ -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<String> = 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)
@@ -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);
}
@@ -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) {
@@ -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>): 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")) {
@@ -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)
}
}
}
@@ -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()
@@ -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<String> = 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<String> = 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<String> = 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<String> = 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<String> = 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<String> = 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<String> = listOf(
BLACK_FORMATTER_SUPPORT,
)
companion object {
const val BLACK_FORMATTER_SUPPORT = "black.formatter.support"
}
}
internal class PythonSDKUpdaterIdsHolder : NotificationIdsHolder {
override fun getNotificationIds(): List<String> = 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<String> = 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<String> = listOf(
PYTHON_INSTALLATION_INTERRUPTED,
)
companion object {
const val PYTHON_INSTALLATION_INTERRUPTED = "python.installation.interrupted"
}
}
internal class PipfileWatcherIdsHolder : NotificationIdsHolder {
override fun getNotificationIds(): List<String> = listOf(
RUN_PIPENV_LOCK_SUGGESTION,
)
companion object {
const val RUN_PIPENV_LOCK_SUGGESTION = "run.pipenv.lock.suggestion"
}
}