in src/Cli/func/Helpers/PythonHelpers.cs [403:459]
internal static async Task<Stream> GetPythonDeploymentPackage(IEnumerable<string> files, string functionAppRoot, bool buildNativeDeps, BuildOption buildOption, string additionalPackages)
{
var reqTxtFile = Path.Combine(functionAppRoot, Constants.RequirementsTxt);
if (!FileSystemHelpers.FileExists(reqTxtFile))
{
throw new CliException($"{Constants.RequirementsTxt} is not found. " +
$"{Constants.RequirementsTxt} is required for python function apps. Please make sure to generate one before publishing.");
}
var packagesLocation = Path.Combine(functionAppRoot, Constants.ExternalPythonPackages);
if (FileSystemHelpers.DirectoryExists(packagesLocation))
{
// Only update packages if checksum of requirements.txt does not match
// If build option is remote, we don't need to verify if packages are in sync, as we need to delete them regardless
if (buildOption != BuildOption.Remote && await ArePackagesInSync(reqTxtFile, packagesLocation))
{
ColoredConsole.WriteLine(WarningColor($"Directory {Constants.ExternalPythonPackages} already in sync with {Constants.RequirementsTxt}. Skipping restoring dependencies..."));
return await ZipHelper.CreateZip(files.Union(FileSystemHelpers.GetFiles(packagesLocation)), functionAppRoot, Enumerable.Empty<string>());
}
ColoredConsole.WriteLine($"Deleting the old {Constants.ExternalPythonPackages} directory");
FileSystemHelpers.DeleteDirectorySafe(packagesLocation);
}
FileSystemHelpers.EnsureDirectory(packagesLocation);
// Only one of the remote build or build-native-deps flag can be chosen
if (buildNativeDeps)
{
if (CommandChecker.CommandExists("docker") && await DockerHelpers.VerifyDockerAccess())
{
await RestorePythonRequirementsDocker(functionAppRoot, packagesLocation, additionalPackages);
}
else
{
throw new CliException("Docker is required to build native dependencies for python function apps");
}
}
else if (buildOption == BuildOption.Remote)
{
// No-ops, python packages will be resolved on the server side
}
else
{
await RestorePythonRequirementsPackapp(functionAppRoot, packagesLocation);
}
// No need to generate and compare .md5 when using remote build
if (buildOption != BuildOption.Remote)
{
// Store a checksum of requirements.txt
var md5FilePath = Path.Combine(packagesLocation, $"{Constants.RequirementsTxt}.md5");
await FileSystemHelpers.WriteAllTextToFileAsync(md5FilePath, SecurityHelpers.CalculateMd5(reqTxtFile));
}
return await ZipHelper.CreateZip(files.Union(FileSystemHelpers.GetFiles(packagesLocation)), functionAppRoot, Enumerable.Empty<string>());
}