mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
small fixes for python requirements generation (PY-18847)
GitOrigin-RevId: c538c855d1bf220981eb430371d55f7f499bf12b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3a6eab25cd
commit
7b8adcf536
@@ -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.
|
||||
|
||||
@@ -4777,4 +4777,5 @@ torch torch-nightly
|
||||
can python-can
|
||||
github PyGithub
|
||||
gitlab python-gitlab
|
||||
tensorflow tensorflow-gpu
|
||||
tensorflow tensorflow-gpu
|
||||
aocd advent-of-code-data
|
||||
@@ -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
|
||||
|
||||
@@ -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<LocalQuickFix> 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<PyRequirement> 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,16 +64,12 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String,
|
||||
}
|
||||
|
||||
// report those requirements that were not changed in base files
|
||||
var inBaseFile = parsed.asSequence()
|
||||
parsed.asSequence()
|
||||
.filter { it.name.toLowerCase() in importedPackages }
|
||||
.map { it to importedPackages[it.name.toLowerCase()] }
|
||||
.map { it to importedPackages.remove(it.name.toLowerCase()) }
|
||||
.filterNot { compatibleVersion(it.first, it.second!!.version, settings.specifyVersion) }
|
||||
.forEach { unchangedInBaseFiles.add(it.first.name) }
|
||||
|
||||
if (settings.keepMatchingSpecifier) {
|
||||
inBaseFile = inBaseFile.filterNot { compatibleVersion(it.first, it.second!!.version, settings.specifyVersion) }
|
||||
}
|
||||
inBaseFile.forEach { unchangedInBaseFiles.add(it.first.name) }
|
||||
|
||||
parsed.forEach { importedPackages.remove(it.name.toLowerCase()) }
|
||||
outputLines.addAll(lines)
|
||||
}
|
||||
else if (parsed.isEmpty()) {
|
||||
@@ -86,7 +82,12 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String,
|
||||
// processing only requirements that are used in the project, discarding others
|
||||
if (name in importedPackages) {
|
||||
val pkg = importedPackages.remove(name)!!
|
||||
if (settings.keepMatchingSpecifier && compatibleVersion(requirement, pkg.version, settings.specifyVersion)) {
|
||||
if (requirement.isEditable || vcsPrefixes.any { line.startsWith(it) }) {
|
||||
// keeping editable and vcs requirements
|
||||
outputLines.addAll(lines)
|
||||
}
|
||||
else if (settings.keepMatchingSpecifier && compatibleVersion(requirement, pkg.version, settings.specifyVersion)) {
|
||||
// existing version separators match the current package version, keeping them
|
||||
outputLines.addAll(lines)
|
||||
}
|
||||
else {
|
||||
@@ -124,7 +125,6 @@ class PyRequirementsFileVisitor(private val importedPackages: MutableMap<String,
|
||||
|
||||
private fun convertToRequirementsEntry(requirement: PyRequirement, settings: PyPackageRequirementsSettings, version: String? = null): String {
|
||||
val packageName = when {
|
||||
requirement.isEditable -> "${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<String,
|
||||
else -> 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<String,
|
||||
|
||||
private fun isFileReference(line: String): Boolean = line.startsWith("-r ") || line.startsWith("--requirement ")
|
||||
private fun isEditableSelf(line: String): Boolean = line.startsWith("--editable .") || line.startsWith("-e .")
|
||||
|
||||
companion object {
|
||||
private val vcsPrefixes = listOf("git:", "git+", "svn+", "hg+", "bzr+")
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ fun syncWithImports(module: Module) {
|
||||
if (matchResult == null) return
|
||||
|
||||
val psiManager = PsiManager.getInstance(module.project)
|
||||
WriteCommandAction.runWriteCommandAction(module.project) {
|
||||
WriteCommandAction.runWriteCommandAction(module.project, PyBundle.message("python.requirements.action.name"), null, {
|
||||
if (requirementsFile == null) {
|
||||
val path = Paths.get(settings.requirementsPath)
|
||||
val location = when {
|
||||
@@ -73,7 +73,7 @@ fun syncWithImports(module: Module) {
|
||||
matchResult.baseFilesOutput.forEach { (file, content) ->
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user