in language-extensions/python/src/PythonLibrarySession.cpp [52:139]
SQLRETURN PythonLibrarySession::InstallLibrary(
string tempFolder,
SQLCHAR *libraryName,
SQLINTEGER libraryNameLength,
SQLCHAR *libraryFile,
SQLINTEGER libraryFileLength,
SQLCHAR *libraryInstallDirectory,
SQLINTEGER libraryInstallDirectoryLength)
{
LOG("PythonLibrarySession::InstallExternalLibrary");
SQLRETURN result = SQL_ERROR;
string errorString;
string libName = string(reinterpret_cast<char *>(libraryName), libraryNameLength);
string installDir = string(reinterpret_cast<char *>(libraryInstallDirectory), libraryInstallDirectoryLength);
installDir = PythonExtensionUtils::NormalizePathString(installDir);
string libFilePath = string(reinterpret_cast<char *>(libraryFile), libraryFileLength);
libFilePath = PythonExtensionUtils::NormalizePathString(libFilePath);
string extractScript = "import zipfile\n"
"with zipfile.ZipFile('" + libFilePath + "') as zip:\n"
" zip.extractall('" + tempFolder + "')";
bp::exec(extractScript.c_str(), m_mainNamespace);
string installPath = "";
// Find the python package inside the zip to use for installation.
//
for (const fs::directory_entry &entry : fs::directory_iterator(tempFolder))
{
string type = entry.path().extension().generic_string();
if (type.compare(".whl") == 0 ||
type.compare(".zip") == 0 ||
type.compare(".gz") == 0)
{
installPath = entry.path().generic_string();
break;
}
}
if (installPath.empty())
{
throw runtime_error("Could not find the package inside the zip - "
"external library must be a python package inside a zip.");
}
string pathToPython = PythonExtensionUtils::GetPathToPython();
// Set the TMPDIR so that pip uses our destination as temp. This allows us to use a
// non-default instance.
// Without this, TMPDIR will have MSSQL##$INSTANCE in the path, and the $ causes problems
// with pip because they interpret $INSTANCE as a variable, not part of the path.
//
string setTemp = "import os;oldtemp = os.environ['TMPDIR'] if 'TMPDIR' in os.environ else None;"
"os.environ['TMPDIR'] = '" + tempFolder + "'";
bp::exec(setTemp.c_str(), m_mainNamespace);
string installScript =
"import subprocess;pipresult = subprocess.run(['" + pathToPython +
"', '-m', 'pip', 'install', '" + installPath +
"', '--no-deps', '--ignore-installed', '--no-cache-dir'"
", '-t', '" + installDir + "']).returncode";
bp::exec(installScript.c_str(), m_mainNamespace);
int pipResult = bp::extract<int>(m_mainNamespace["pipresult"]);
string resetTemp = "if oldtemp: \n"
" os.environ['TMPDIR'] = oldtemp\n"
"else:\n"
" del os.environ['TMPDIR']";
bp::exec(resetTemp.c_str(), m_mainNamespace);
if (pipResult != 0)
{
throw runtime_error("Pip failed to install the package with exit code " +
to_string(pipResult));
}
result = SQL_SUCCESS;
return result;
}