mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-78995: (finished): Show promo in interpreter settings, do not throw exception from run configurations.
Open Interpreter settings, try remote and local in free mode. Try running run config with remote interpreter in free tier GitOrigin-RevId: e6c611826e942c49f5b576ea769a044357cf6146
This commit is contained in:
committed by
intellij-monorepo-bot
parent
115d00ef1c
commit
ec660d11c2
+1
-1
@@ -178,7 +178,7 @@ public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm {
|
||||
}
|
||||
|
||||
public void updateSdkList(boolean preserveSelection, PyConfigurableInterpreterList myInterpreterList) {
|
||||
myPythonSdks = myInterpreterList.getAllPythonSdks(myProject, null);
|
||||
myPythonSdks = myInterpreterList.getAllPythonSdks(myProject, null, false);
|
||||
Sdk selection = preserveSelection ? (Sdk)content.interpreterComboBox.getSelectedItem() : null;
|
||||
if (!myPythonSdks.contains(selection)) {
|
||||
selection = null;
|
||||
|
||||
@@ -71,20 +71,38 @@ public final class PythonSdkUtil {
|
||||
private static final Key<PySkeletonHeader> CACHED_SKELETON_HEADER = Key.create("CACHED_SKELETON_HEADER");
|
||||
|
||||
public static boolean isPythonSdk(@NotNull Sdk sdk) {
|
||||
return isPythonSdk(sdk, false);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static boolean isPythonSdk(@NotNull Sdk sdk, boolean allowRemoteInFreeTier) {
|
||||
if (!PyNames.PYTHON_SDK_ID_NAME.equals(sdk.getSdkType().getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// PY-79923: Should explicitly filter sdks created while pro was active
|
||||
if (PlatformUtils.isPyCharm() && !PlatformUtils.isDataSpell()) {
|
||||
return !isRemote(sdk) || !PluginManagerCore.isDisabled(ULTIMATE_PLUGIN_ID);
|
||||
if (isFreeTier()) {
|
||||
return allowRemoteInFreeTier || (!isRemote(sdk));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static @Unmodifiable List<Sdk> getAllSdks() {
|
||||
return ContainerUtil.filter(ProjectJdkTable.getInstance().getAllJdks(), PythonSdkUtil::isPythonSdk);
|
||||
/**
|
||||
* @return PyCharm with Pro mode disabled
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static boolean isFreeTier() {
|
||||
return PlatformUtils.isPyCharm() && (!PlatformUtils.isDataSpell()) && PluginManagerCore.isDisabled(ULTIMATE_PLUGIN_ID);
|
||||
}
|
||||
|
||||
public static @Unmodifiable @NotNull List<@NotNull Sdk> getAllSdks() {
|
||||
return getAllSdks(false);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @Unmodifiable @NotNull List<@NotNull Sdk> getAllSdks(boolean allowRemoteInFreeTier) {
|
||||
return ContainerUtil.filter(ProjectJdkTable.getInstance().getAllJdks(), sdk -> isPythonSdk(sdk, allowRemoteInFreeTier));
|
||||
}
|
||||
|
||||
private static @Nullable PySkeletonHeader readSkeletonHeader(@NotNull VirtualFile file, @NotNull Sdk pythonSdk) {
|
||||
|
||||
@@ -4,7 +4,6 @@ package com.jetbrains.python.configuration;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -24,7 +23,8 @@ import com.intellij.openapi.ui.popup.ListPopup;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.ComboboxSpeedSearch;
|
||||
import com.intellij.ui.components.DropDownLink;
|
||||
import com.intellij.util.NullableConsumer;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
@@ -47,6 +47,7 @@ import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.jetbrains.python.sdk.PySdkRenderingKt.groupModuleSdksByTypes;
|
||||
import static com.jetbrains.python.sdk.PythonSdkUtil.isRemote;
|
||||
|
||||
public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
|
||||
@@ -65,6 +66,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
private final @NotNull ComboBox<Object> mySdkCombo;
|
||||
|
||||
private final @NotNull PyInstalledPackagesPanel myPackagesPanel;
|
||||
private final @Nullable PyPanelWithPromo myPanelWithPromo;
|
||||
|
||||
private final @Nullable Disposable myDisposable;
|
||||
|
||||
@@ -85,6 +87,8 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
final PackagesNotificationPanel packagesNotificationPanel = new PyPackagesNotificationPanel();
|
||||
myPackagesPanel = new PyInstalledPackagesPanel(myProject, packagesNotificationPanel);
|
||||
myPackagesPanel.setShowGrid(false);
|
||||
boolean freeTier = PythonSdkUtil.isFreeTier();
|
||||
myPanelWithPromo = freeTier ? new PyPanelWithPromo(myPackagesPanel) : null;
|
||||
|
||||
final PyCustomSdkUiProvider customUiProvider = PyCustomSdkUiProvider.getInstance();
|
||||
myDisposable = customUiProvider == null ? null : Disposer.newDisposable();
|
||||
@@ -95,7 +99,9 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
additionalAction = new DropDownLink<>(PyBundle.message("active.sdk.dialog.link.add.interpreter.text"),
|
||||
link -> createAddInterpreterPopup(project, module, link, this::updateSdkListAndSelect));
|
||||
|
||||
myMainPanel = buildPanel(project, mySdkCombo, additionalAction, myPackagesPanel, packagesNotificationPanel, customizer);
|
||||
myMainPanel =
|
||||
buildPanel(project, mySdkCombo, additionalAction, freeTier ? myPanelWithPromo.getPanel() : myPackagesPanel, packagesNotificationPanel,
|
||||
customizer);
|
||||
|
||||
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
|
||||
myProjectSdksModel = myInterpreterList.getModel();
|
||||
@@ -160,9 +166,10 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
private static @NotNull JPanel buildPanel(@NotNull Project project,
|
||||
@NotNull ComboBox<?> sdkComboBox,
|
||||
@NotNull JComponent additionalAction,
|
||||
@NotNull PyInstalledPackagesPanel installedPackagesPanel,
|
||||
@NotNull JPanel promotionPanel,
|
||||
@NotNull PackagesNotificationPanel packagesNotificationPanel,
|
||||
@Nullable Pair<PyCustomSdkUiProvider, Disposable> customizer) {
|
||||
|
||||
final JPanel result = new JPanel(new GridBagLayout());
|
||||
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
@@ -196,7 +203,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
c.gridwidth = 3;
|
||||
c.gridheight = GridBagConstraints.RELATIVE;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
result.add(installedPackagesPanel, c);
|
||||
result.add(promotionPanel, c);
|
||||
|
||||
c.gridheight = GridBagConstraints.REMAINDER;
|
||||
c.gridx = 0;
|
||||
@@ -218,6 +225,18 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
|
||||
protected void onSdkSelected() {
|
||||
final Sdk sdk = getOriginalSelectedSdk();
|
||||
|
||||
if (sdk != null) {
|
||||
// Non-null means we are in free tier mode, so must switch between packages and promo panel
|
||||
if (myPanelWithPromo != null) {
|
||||
boolean remote = isRemote(sdk);
|
||||
myPanelWithPromo.setPromoMode(remote);
|
||||
if (remote) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshPackages(sdk);
|
||||
}
|
||||
|
||||
@@ -273,8 +292,9 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
Sdk sdk = null;
|
||||
if (myModule == null) {
|
||||
sdk = ProjectRootManager.getInstance(myProject).getProjectSdk();
|
||||
} else {
|
||||
sdk = ModuleRootManager.getInstance(myModule).getSdk();
|
||||
}
|
||||
else {
|
||||
sdk = ModuleRootManager.getInstance(myModule).getSdk();
|
||||
}
|
||||
|
||||
if (sdk != null && PythonSdkUtil.isPythonSdk(sdk)) {
|
||||
@@ -315,7 +335,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
}
|
||||
|
||||
protected @NotNull List<Sdk> getAvailableSdks() {
|
||||
return myInterpreterList.getAllPythonSdks(myProject, myModule);
|
||||
return myInterpreterList.getAllPythonSdks(myProject, myModule, true);
|
||||
}
|
||||
|
||||
private void updateSdkListAndSelect(@Nullable Sdk selectedSdk) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory;
|
||||
import com.jetbrains.python.run.TargetConfigurationWithLocalFsAccessExKt;
|
||||
import com.jetbrains.python.sdk.*;
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -57,14 +58,15 @@ public final class PyConfigurableInterpreterList {
|
||||
/**
|
||||
* @param module if not null and module resides on certain target, returns only SDKs for this target
|
||||
*/
|
||||
public @NotNull List<Sdk> getAllPythonSdks(final @Nullable Project project, @Nullable Module module) {
|
||||
@ApiStatus.Internal
|
||||
public @NotNull List<Sdk> getAllPythonSdks(final @Nullable Project project, @Nullable Module module, boolean allowRemoteInFreeTier) {
|
||||
var targetModuleSitsOn = (module != null)
|
||||
? PythonInterpreterTargetEnvironmentFactory.Companion.getTargetModuleResidesOn(module)
|
||||
: null;
|
||||
|
||||
List<Sdk> result = new ArrayList<>();
|
||||
for (Sdk sdk : getModel().getSdks()) {
|
||||
if (!PythonSdkUtil.isPythonSdk(sdk)) {
|
||||
if (!PythonSdkUtil.isPythonSdk(sdk, allowRemoteInFreeTier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -81,7 +83,7 @@ public final class PyConfigurableInterpreterList {
|
||||
}
|
||||
|
||||
public @NotNull List<Sdk> getAllPythonSdks() {
|
||||
return getAllPythonSdks(null, null);
|
||||
return getAllPythonSdks(null, null, false);
|
||||
}
|
||||
|
||||
private static class PyInterpreterComparator implements Comparator<Sdk> {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.configuration
|
||||
|
||||
import com.intellij.openapi.observable.properties.PropertyGraph
|
||||
import com.intellij.openapi.observable.util.not
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import com.jetbrains.python.createPromoPanel
|
||||
import javax.swing.JPanel
|
||||
|
||||
/**
|
||||
* Shows either promo-panel (if [promoMode]) or [whenPromoDisabled]
|
||||
*/
|
||||
internal class PyPanelWithPromo(private val whenPromoDisabled: JPanel) {
|
||||
private val root = PropertyGraph()
|
||||
private val _promoMode = root.property(false)
|
||||
var promoMode: Boolean by _promoMode
|
||||
val panel: JPanel = panel {
|
||||
row {
|
||||
cell(createPromoPanel()).resizableColumn().align(AlignY.CENTER + AlignX.FILL).applyToComponent { putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = false, bottom = false)) }
|
||||
}.resizableRow().visibleIf(_promoMode)
|
||||
row {
|
||||
cell(whenPromoDisabled).resizableColumn().align(AlignY.FILL + AlignX.FILL).applyToComponent { putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = false, bottom = false)) }
|
||||
}.resizableRow().visibleIf(_promoMode.not())
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ internal class PythonInterpreterMasterDetails(private val moduleOrProject: Modul
|
||||
}
|
||||
|
||||
private val allPythonSdksInEdit: List<Sdk>
|
||||
get() = pythonConfigurableInterpreterList.getAllPythonSdks(project, module)
|
||||
get() = pythonConfigurableInterpreterList.getAllPythonSdks(project, module, false)
|
||||
|
||||
override fun reset() {
|
||||
pythonPathsModified = false
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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
|
||||
|
||||
import com.intellij.ide.browsers.BrowserLauncher
|
||||
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.ui.JBFont
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import javax.swing.JPanel
|
||||
|
||||
/**
|
||||
* Panel that suggests user to buy Pro version
|
||||
*/
|
||||
@RequiresEdt
|
||||
internal fun createPromoPanel(): JPanel = panel {
|
||||
row {
|
||||
icon(PythonIcons.Python.Pycharm32).resizableColumn().align(AlignX.RIGHT + AlignY.TOP)
|
||||
panel {
|
||||
row {
|
||||
@Suppress("DialogTitleCapitalization") // PyCharm Pro is literally how we want to see it
|
||||
text(PyBundle.message("pycharm.free.mode.upgrade.title")).align(AlignY.BOTTOM).applyToComponent {
|
||||
font = JBFont.h1()
|
||||
}
|
||||
}
|
||||
row {
|
||||
text(PyBundle.message("pycharm.free.mode.upgrade.body")).align(AlignY.TOP).applyToComponent { putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = false, bottom = false)) }
|
||||
}
|
||||
row {
|
||||
button(PyBundle.message("pycharm.free.mode.upgrade.button")) {
|
||||
BrowserLauncher.instance.open("https://www.jetbrains.com/pycharm/buy/?section=commercial&billing=yearly")
|
||||
}.applyToComponent {
|
||||
putClientProperty(DarculaButtonUI.DEFAULT_STYLE_KEY, true)
|
||||
}
|
||||
}
|
||||
}.resizableColumn().align(AlignX.LEFT)
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ import com.jetbrains.python.debugger.PyTargetPathMapper;
|
||||
import com.jetbrains.python.facet.LibraryContributingFacet;
|
||||
import com.jetbrains.python.facet.PythonPathContributingFacet;
|
||||
import com.jetbrains.python.library.PythonLibraryType;
|
||||
import com.jetbrains.python.packaging.PyExecutionException;
|
||||
import com.jetbrains.python.remote.PyRemotePathMapper;
|
||||
import com.jetbrains.python.remote.PyRemoteSdkAdditionalData;
|
||||
import com.jetbrains.python.run.target.HelpersAwareTargetEnvironmentRequest;
|
||||
@@ -1047,22 +1048,21 @@ public abstract class PythonCommandLineState extends CommandLineState {
|
||||
return interpreterPath;
|
||||
}
|
||||
|
||||
private @NotNull HelpersAwareTargetEnvironmentRequest getPythonTargetInterpreter() {
|
||||
return getPythonTargetInterpreter(myConfig.getProject(), getSdk());
|
||||
private @NotNull HelpersAwareTargetEnvironmentRequest getPythonTargetInterpreter() throws ExecutionException {
|
||||
Sdk sdk = getSdk();
|
||||
if (sdk == null) {
|
||||
throw new PyExecutionException(PyBundle.message("runcfg.error.message.cannot.find.python.interpreter"));
|
||||
}
|
||||
return getPythonTargetInterpreter(myConfig.getProject(), sdk);
|
||||
}
|
||||
|
||||
public static @NotNull HelpersAwareTargetEnvironmentRequest getPythonTargetInterpreter(@NotNull Project project, @Nullable Sdk sdk) {
|
||||
if (sdk == null) {
|
||||
throw new IllegalStateException("SDK is not defined for Run Configuration");
|
||||
}
|
||||
else {
|
||||
public static @NotNull HelpersAwareTargetEnvironmentRequest getPythonTargetInterpreter(@NotNull Project project, @NotNull Sdk sdk) {
|
||||
HelpersAwareTargetEnvironmentRequest helpersAwareTargetRequest =
|
||||
PythonInterpreterTargetEnvironmentFactory.findPythonTargetInterpreter(sdk, project);
|
||||
if (helpersAwareTargetRequest == null) {
|
||||
throw new IllegalStateException("Cannot find execution environment for SDK " + sdk);
|
||||
}
|
||||
return helpersAwareTargetRequest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,9 +11,7 @@ import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAc
|
||||
import com.intellij.facet.impl.invalid.FacetIgnorer
|
||||
import com.intellij.facet.impl.invalid.InvalidFacet
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.browsers.BrowserLauncher
|
||||
import com.intellij.ide.plugins.PluginManager
|
||||
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.openapi.options.SettingsEditor
|
||||
@@ -21,12 +19,10 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.JDOMUtil
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.util.WriteExternalException
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.ui.JBFont
|
||||
import com.jetbrains.python.PYTHON_PROF_PLUGIN_ID
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.createPromoPanel
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.annotations.NonNls
|
||||
@@ -40,29 +36,7 @@ private class PythonLockedRunConfigurationEditor : SettingsEditor<PythonLockedRu
|
||||
|
||||
override fun applyEditorTo(s: PythonLockedRunConfiguration) {}
|
||||
|
||||
protected override fun createEditor(): JComponent = panel {
|
||||
row {
|
||||
icon(PythonIcons.Python.Pycharm32).resizableColumn().align(AlignX.RIGHT + AlignY.TOP)
|
||||
panel {
|
||||
row {
|
||||
@Suppress("DialogTitleCapitalization") // PyCharm Pro is literally how we want to see it
|
||||
text(PyBundle.message("pycharm.free.mode.upgrade.title")).align(AlignY.BOTTOM).applyToComponent {
|
||||
font = JBFont.h1()
|
||||
}
|
||||
}
|
||||
row {
|
||||
text(PyBundle.message("pycharm.free.mode.upgrade.body")).align(AlignY.TOP).applyToComponent { putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = false, bottom = false)) }
|
||||
}
|
||||
row {
|
||||
button(PyBundle.message("pycharm.free.mode.upgrade.button")) {
|
||||
BrowserLauncher.instance.open("https://www.jetbrains.com/pycharm/buy/?section=commercial&billing=yearly")
|
||||
}.applyToComponent {
|
||||
putClientProperty(DarculaButtonUI.DEFAULT_STYLE_KEY, true)
|
||||
}
|
||||
}
|
||||
}.resizableColumn().align(AlignX.LEFT)
|
||||
}
|
||||
}
|
||||
protected override fun createEditor(): JComponent = createPromoPanel()
|
||||
}
|
||||
|
||||
private class PythonLockedRunConfiguration(val configProject: Project, val configFactory: ConfigurationFactory)
|
||||
|
||||
@@ -56,7 +56,7 @@ class PySdkPopupFactory(val module: Module) {
|
||||
|
||||
val interpreterList = PyConfigurableInterpreterList.getInstance(module.project)
|
||||
val moduleSdksByTypes = SlowOperations.knownIssue("PY-76167").use {
|
||||
groupModuleSdksByTypes(interpreterList.getAllPythonSdks(module.project, module), module) {
|
||||
groupModuleSdksByTypes(interpreterList.getAllPythonSdks(module.project, module, false), module) {
|
||||
!it.sdkSeemsValid ||
|
||||
PythonSdkType.hasInvalidRemoteCredentials(it) ||
|
||||
PythonSdkType.isIncompleteRemote(it) ||
|
||||
|
||||
Reference in New Issue
Block a user