Python: detect virtual environments for documents and projects

After opening a document or project the directory hierarchy is looked up
for a Scripts/(activate && python.exe) on windows or bin/(activate &&
python) on unix. This is the usual structure of python virtual
environments. If such a folder is found add the python from that folder
to the list of configured interpreters in the settings, set it as the
current interpreter for the project and try to open the corresponding
language server.

Change-Id: I038c309ea2988f9370194330d250d1515beac0a0
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2019-10-25 09:15:59 +02:00
parent 519fc4ec72
commit 6664d78ded
4 changed files with 79 additions and 6 deletions

View File

@@ -211,6 +211,7 @@ public:
InterpreterOptionsPage();
void setInterpreter(const QList<Interpreter> &interpreters) { m_interpreters = interpreters; }
void addInterpreter(const Interpreter &interpreter) { m_interpreters << interpreter; }
QList<Interpreter> interpreters() const { return m_interpreters; }
void setDefaultInterpreter(const QString &defaultId)
{ m_defaultInterpreterId = defaultId; }
@@ -278,6 +279,7 @@ Interpreter::Interpreter(const FilePath &python, const QString &defaultName, boo
{
SynchronousProcess pythonProcess;
pythonProcess.setProcessChannelMode(QProcess::MergedChannels);
pythonProcess.setTimeoutS(1);
SynchronousProcessResponse response = pythonProcess.runBlocking(
CommandLine(python, {"--version"}));
if (response.result == SynchronousProcessResponse::Finished)
@@ -467,9 +469,15 @@ void PythonSettings::setInterpreter(const QList<Interpreter> &interpreters, cons
{
interpreterOptionsPage().setInterpreter(interpreters);
interpreterOptionsPage().setDefaultInterpreter(defaultId);
toSettings(Core::ICore::settings(), {interpreters, defaultId});
if (QTC_GUARD(settingsInstance))
emit settingsInstance->interpretersChanged(interpreters, defaultId);
saveSettings();
}
void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefault)
{
interpreterOptionsPage().addInterpreter(interpreter);
if (isDefault)
interpreterOptionsPage().setDefaultInterpreter(interpreter.id);
saveSettings();
}
PythonSettings *PythonSettings::instance()
@@ -478,6 +486,52 @@ PythonSettings *PythonSettings::instance()
return settingsInstance;
}
QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)
{
QList<Interpreter> result;
QDir dir = path.toFileInfo().isDir() ? QDir(path.toString()) : path.toFileInfo().dir();
if (dir.exists()) {
const QString venvPython = HostOsInfo::withExecutableSuffix("python");
const QString activatePath = HostOsInfo::isWindowsHost() ? QString{"Scripts"}
: QString{"bin"};
do {
for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
if (dir.cd(directory)) {
if (dir.cd(activatePath)) {
if (dir.exists("activate") && dir.exists(venvPython)) {
FilePath python = FilePath::fromString(dir.absoluteFilePath(venvPython));
dir.cdUp();
const QString defaultName = QString("Python (%1 Virtual Environment)")
.arg(dir.dirName());
Interpreter interpreter
= Utils::findOrDefault(PythonSettings::interpreters(),
Utils::equal(&Interpreter::command, python));
if (interpreter.command.isEmpty()) {
interpreter = Interpreter(python, defaultName);
PythonSettings::addInterpreter(interpreter);
}
result << interpreter;
} else {
dir.cdUp();
}
}
dir.cdUp();
}
}
} while (dir.cdUp());
}
return result;
}
void PythonSettings::saveSettings()
{
const QList<Interpreter> &interpreters = interpreterOptionsPage().interpreters();
const QString &defaultId = interpreterOptionsPage().defaultInterpreter().id;
toSettings(Core::ICore::settings(), {interpreters, defaultId});
if (QTC_GUARD(settingsInstance))
emit settingsInstance->interpretersChanged(interpreters, defaultId);
}
QList<Interpreter> PythonSettings::interpreters()
{
return interpreterOptionsPage().interpreters();