diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index f396dac68034..9113ec277551 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1496,6 +1496,7 @@ integrated.tools.configurable.docstrings=Docstrings integrated.tools.configurable.restructuredtext=reStructuredText integrated.tools.configurable.packaging=Packaging integrated.tools.configurable.testing=Testing +integrated.tools.configurable.pipenv=Pipenv command.line.parser.error.message=Space between argument is its value is unexpected python.template.package.installation.validation.message="{0} will be installed on selected interpreter" @@ -1521,9 +1522,9 @@ python.requirements.version.separator.compatible=Compatible version python.requirements.remove.unused=Remove unused requirements python.requirements.modify.base.files=Modify base files (defined with -r or --requirement) python.requirements.keep.matching.specifier=Keep existing version specifier if it matches the current version -python.requirements.quickfix.family.name=Synchronize requirements with project imports +python.requirements.quickfix.family.name=Add imported packages to requirements... python.requirements.analyzing.imports.title=Analyzing imports in project -python.requirements.requirements.file.dialog.header=Select requirements file +python.requirements.action.name=Sync Python requirements python.requirements.error.ends.with.slash=Error parsing requirements: file ends with '/' symbol. python.requirements.error.no.packages=Error retrieving the list of packages. python.requirements.error.no.interpreter=Configured interpreter required to synchronize requirements. diff --git a/python/python-psi-impl/resources/tools/packages b/python/python-psi-impl/resources/tools/packages index 1224e317d3bb..23ea08646f27 100644 --- a/python/python-psi-impl/resources/tools/packages +++ b/python/python-psi-impl/resources/tools/packages @@ -4777,4 +4777,5 @@ torch torch-nightly can python-can github PyGithub gitlab python-gitlab -tensorflow tensorflow-gpu \ No newline at end of file +tensorflow tensorflow-gpu +aocd advent-of-code-data \ No newline at end of file diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java index 11e5f8480866..053ae60c864e 100644 --- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java @@ -124,6 +124,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable { myRestPanel.setBorder(IdeBorderFactory.createTitledBorder(PyBundle.message("integrated.tools.configurable.restructuredtext"))); myPackagingPanel.setBorder(IdeBorderFactory.createTitledBorder(PyBundle.message("integrated.tools.configurable.packaging"))); myTestsPanel.setBorder(IdeBorderFactory.createTitledBorder(PyBundle.message("integrated.tools.configurable.testing"))); + myPipEnvPanel.setBorder(IdeBorderFactory.createTitledBorder(PyBundle.message("integrated.tools.configurable.pipenv"))); } @NotNull diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java index 94e2afc6e794..fb576c0c751b 100644 --- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java @@ -40,7 +40,6 @@ import com.jetbrains.python.sdk.PySdkExtKt; import com.jetbrains.python.sdk.PythonSdkUtil; import com.jetbrains.python.sdk.pipenv.PipEnvInstallQuickFix; import com.jetbrains.python.sdk.pipenv.PipenvKt; -import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -224,24 +223,14 @@ public class PyPackageRequirementsInspection extends PyInspection { } } - final List quickFixes = new ArrayList<>(); - - StreamEx - .of(packageName) - .append(possiblePyPIPackageNames) - .filter(PyPIPackageUtil.INSTANCE::isInPyPI) - .map(name -> new AddToRequirementsFix(packageManager, module, name, LanguageLevel.forElement(importedExpression))) - .forEach(quickFixes::add); - - quickFixes.add(new PyGenerateRequirementsFileQuickFix(module)); - - quickFixes.add(new IgnoreRequirementFix(Collections.singleton(packageName))); + final LocalQuickFix[] fixes = { new PyGenerateRequirementsFileQuickFix(module), + new IgnoreRequirementFix(Collections.singleton(packageName))}; registerProblem(packageReferenceExpression, String.format("Package containing module '%s' is not listed in project requirements", packageName), ProblemHighlightType.WEAK_WARNING, null, - quickFixes.toArray(LocalQuickFix.EMPTY_ARRAY)); + fixes); } } } @@ -664,77 +653,4 @@ public class PyPackageRequirementsInspection extends PyInspection { } } } - - private static class AddToRequirementsFix implements LocalQuickFix { - - @NotNull - private final PyPackageManager myPackageManager; - - @NotNull - private final Module myModule; - - @NotNull - private final String myPackageName; - - @NotNull - private final LanguageLevel myLanguageLevel; - - private AddToRequirementsFix(@NotNull PyPackageManager packageManager, - @NotNull Module module, - @NotNull String packageName, - @NotNull LanguageLevel languageLevel) { - myPackageManager = packageManager; - myModule = module; - myPackageName = packageName; - myLanguageLevel = languageLevel; - } - - @Override - public boolean startInWriteAction() { - return false; - } - - @Nls(capitalization = Nls.Capitalization.Sentence) - @NotNull - @Override - public String getFamilyName() { - return PyBundle.message("INSP.package.requirements.add.requirement"); - } - - @Nls(capitalization = Nls.Capitalization.Sentence) - @NotNull - @Override - public String getName() { - return String.format("Add requirement '%s' to %s", myPackageName, calculateTarget()); - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final List requirements = myPackageManager.getRequirements(myModule); - if (requirements != null && ContainerUtil.exists(requirements, r -> r.getName().equals(myPackageName))) return; - - CommandProcessor.getInstance().executeCommand( - project, - () -> ApplicationManager.getApplication().runWriteAction( - () -> PyPackageUtil.addRequirementToTxtOrSetupPy(myModule, myPackageName, myLanguageLevel) - ), - getName(), - null - ); - } - - @NotNull - private String calculateTarget() { - final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(myModule); - if (requirementsTxt != null) { - return requirementsTxt.getName(); - } - else if (PyPackageUtil.findSetupCall(myModule) != null) { - return "setup.py"; - } - else { - return "project requirements"; - } - } - } } diff --git a/python/src/com/jetbrains/python/packaging/PyRequirementsFileVisitor.kt b/python/src/com/jetbrains/python/packaging/PyRequirementsFileVisitor.kt index ef50f1ed2afb..2009f9a5b43d 100644 --- a/python/src/com/jetbrains/python/packaging/PyRequirementsFileVisitor.kt +++ b/python/src/com/jetbrains/python/packaging/PyRequirementsFileVisitor.kt @@ -64,16 +64,12 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap "${requirement.installOptions[0]} ${requirement.installOptions[1]}${requirement.extras}" settings.specifyVersion -> when { version != null -> requirement.name + requirement.extras + settings.versionSpecifier.separator + version else -> requirement.presentableText @@ -132,11 +132,9 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap requirement.name + requirement.extras } - val optionsToDrop = if (requirement.isEditable) 2 else 1 - - if (requirement.installOptions.size == optionsToDrop) return packageName + if (requirement.installOptions.size == 1) return packageName val offset = " ".repeat(packageName.length + 1) - val installOptions = requirement.installOptions.drop(optionsToDrop).joinToString(separator = "\\\n$offset") + val installOptions = requirement.installOptions.drop(1).joinToString(separator = "\\\n$offset") return "$packageName $installOptions" } @@ -145,4 +143,8 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap documentManager.getDocument(file)!!.setText(content.joinToString("\n")) } - } + }, emptyArray()) psiManager.findFile(requirementsFile!!)?.navigate(true) if (matchResult.unhandledLines.isNotEmpty()) { val text = PyBundle.message("python.requirements.warning.unhandled.lines", matchResult.unhandledLines.joinToString(", ")) @@ -172,7 +172,7 @@ private fun showSpecifyRequirementsFileDialog(project: Project, settings: PyPack { settings.keepMatchingSpecifier = it }) } } - val dialog = dialog(title = PyBundle.message("python.requirements.requirements.file.dialog.header"), + val dialog = dialog(title = PyBundle.message("python.requirements.action.name"), panel = panel, resizable = true, project = project) diff --git a/python/testSrc/com/jetbrains/python/packaging/PyRequirementsGenerationTest.kt b/python/testSrc/com/jetbrains/python/packaging/PyRequirementsGenerationTest.kt index 1430ec30ad24..24cec8ffebf6 100644 --- a/python/testSrc/com/jetbrains/python/packaging/PyRequirementsGenerationTest.kt +++ b/python/testSrc/com/jetbrains/python/packaging/PyRequirementsGenerationTest.kt @@ -10,7 +10,7 @@ import org.easymock.EasyMock class PyRequirementsGenerationTest : PyTestCase() { - var oldPackageManagers: PyPackageManagers? = null + private var oldPackageManagers: PyPackageManagers? = null fun testNewFileGeneration() = doTest() fun testNewFileWithoutVersion() = doTest(PyRequirementsVersionSpecifierType.NO_VERSION) @@ -64,6 +64,7 @@ class PyRequirementsGenerationTest : PyTestCase() { syncWithImports(myFixture.module) myFixture.checkResultByFile("$testName/new_${settings.requirementsPath}") } + assertProjectFilesNotParsed(myFixture.file) } finally { settings.requirementsPath = oldRequirementsPath