Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
#### Changes

- <entry>
- Add Dockerfile for python 3.13 local build environment (#4611)
3 changes: 3 additions & 0 deletions src/Cli/func/Azure.Functions.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
<EmbeddedResource Include="StaticResources\Dockerfile.python3.13">
<LogicalName>$(AssemblyName).Dockerfile.python3.13</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="StaticResources\Dockerfile.python3.13-buildenv">
<LogicalName>$(AssemblyName).Dockerfile.python3.13-buildenv</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="StaticResources\Dockerfile.javascript">
<LogicalName>$(AssemblyName).Dockerfile.javascript</LogicalName>
</EmbeddedResource>
Expand Down
1 change: 0 additions & 1 deletion src/Cli/func/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ public static class DockerImages
public const string LinuxPython310ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.10-buildenv";
public const string LinuxPython311ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.11-buildenv";
public const string LinuxPython312ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.12-buildenv";
public const string LinuxPython313ImageAmd64 = "mcr.microsoft.com/azure-functions/python:4-python3.13-buildenv";
}

public static class StaticResourcesNames
Expand Down
11 changes: 11 additions & 0 deletions src/Cli/func/Helpers/DockerImageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

namespace Azure.Functions.Cli.Helpers;

public class DockerImageInfo
{
public string ImageName { get; set; }

public bool CanPull { get; set; }
}
61 changes: 45 additions & 16 deletions src/Cli/func/Helpers/PythonHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,8 @@ public static async Task<Stream> ZipToSquashfsStream(Stream stream)
string containerId = null;
try
{
string dockerImage = await ChoosePythonBuildEnvImage();
DockerImageInfo result = await ChoosePythonBuildEnvImage();
string dockerImage = result.ImageName;
containerId = await DockerHelpers.DockerRun(dockerImage, command: "sleep infinity");

await DockerHelpers.CopyToContainer(containerId, tmpFile, $"/file.zip");
Expand Down Expand Up @@ -494,14 +495,19 @@ private static async Task RestorePythonRequirementsDocker(string functionAppRoot
var dockerSkipPullFlagSetting = Environment.GetEnvironmentVariable(Constants.PythonDockerImageSkipPull);
var dockerRunSetting = Environment.GetEnvironmentVariable(Constants.PythonDockerRunCommand);

bool canPull = true;
string dockerImage = pythonDockerImageSetting;
if (string.IsNullOrEmpty(dockerImage))
{
dockerImage = await ChoosePythonBuildEnvImage();
DockerImageInfo result = await ChoosePythonBuildEnvImage();
dockerImage = result.ImageName;
canPull = result.CanPull;
}

if (string.IsNullOrEmpty(dockerSkipPullFlagSetting) ||
!(dockerSkipPullFlagSetting.Equals("true", StringComparison.OrdinalIgnoreCase) || dockerSkipPullFlagSetting == "1"))
if (canPull &&
(string.IsNullOrEmpty(dockerSkipPullFlagSetting) ||
!(dockerSkipPullFlagSetting.Equals("true", StringComparison.OrdinalIgnoreCase) ||
dockerSkipPullFlagSetting == "1")))
{
await DockerHelpers.DockerPull(dockerImage);
}
Expand Down Expand Up @@ -547,10 +553,30 @@ private static async Task RestorePythonRequirementsDocker(string functionAppRoot
}
}

private static async Task<string> ChoosePythonBuildEnvImage()
private static async Task<DockerImageInfo> ChoosePythonBuildEnvImage()
{
WorkerLanguageVersionInfo workerInfo = await GetEnvironmentPythonVersion();
return GetBuildNativeDepsEnvironmentImage(workerInfo);
var (image, isLocal) = await GetBuildNativeDepsEnvironmentImage(workerInfo);

if (isLocal)
{
// Setup image tag and content
string imageContent = image;
image = $"azure-functions/python:4-python{workerInfo.Major}.{workerInfo.Minor}-buildenv";

// Prepare temporary directory for docker build context
string tempDockerfileDirecotry = Path.Combine(Path.GetTempPath(), $"{image}-docker");
FileSystemHelpers.EnsureDirectory(tempDockerfileDirecotry);
string tempDockerfile = Path.Combine(tempDockerfileDirecotry, "Dockerfile");

// Write Dockerfile content to temporary file
await FileSystemHelpers.WriteAllTextToFileAsync(tempDockerfile, imageContent);

// Build the image
await DockerHelpers.DockerBuild(image, tempDockerfileDirecotry);
}

return new DockerImageInfo { ImageName = image, CanPull = !isLocal };
}

private static string CopyToTemp(IEnumerable<string> files, string rootPath)
Expand Down Expand Up @@ -596,32 +622,35 @@ public static Task<string> GetDockerInitFileContent(WorkerLanguageVersionInfo in
return StaticResources.DockerfilePython37;
}

private static string GetBuildNativeDepsEnvironmentImage(WorkerLanguageVersionInfo info)
// Build environment images for building native dependencies for python function apps
private static async Task<(string Image, bool IsLocal)> GetBuildNativeDepsEnvironmentImage(WorkerLanguageVersionInfo info)
{
if (info?.Major == 3)
{
switch (info?.Minor)
{
case 6:
return Constants.DockerImages.LinuxPython36ImageAmd64;
return (DockerImages.LinuxPython36ImageAmd64, false);
case 7:
return Constants.DockerImages.LinuxPython37ImageAmd64;
return (DockerImages.LinuxPython37ImageAmd64, false);
case 8:
return Constants.DockerImages.LinuxPython38ImageAmd64;
return (DockerImages.LinuxPython38ImageAmd64, false);
case 9:
return Constants.DockerImages.LinuxPython39ImageAmd64;
return (DockerImages.LinuxPython39ImageAmd64, false);
case 10:
return Constants.DockerImages.LinuxPython310ImageAmd64;
return (DockerImages.LinuxPython310ImageAmd64, false);
case 11:
return Constants.DockerImages.LinuxPython311ImageAmd64;
return (DockerImages.LinuxPython311ImageAmd64, false);
case 12:
return Constants.DockerImages.LinuxPython312ImageAmd64;
return (DockerImages.LinuxPython312ImageAmd64, false);

// From Python 3.13 onwards, we use a Dockerfile to build the image locally
case 13:
return Constants.DockerImages.LinuxPython313ImageAmd64;
return (await StaticResources.DockerfilePython313BuildEnv, true);
}
}

return Constants.DockerImages.LinuxPython312ImageAmd64;
return (DockerImages.LinuxPython312ImageAmd64, false);
}

private static bool IsVersionSupported(WorkerLanguageVersionInfo info)
Expand Down
43 changes: 43 additions & 0 deletions src/Cli/func/StaticResources/Dockerfile.python3.13-buildenv
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
FROM mcr.microsoft.com/oryx/python:3.13-debian-bookworm-20250724.1

ENV LANG=C.UTF-8 \
ACCEPT_EULA=Y \
AzureWebJobsScriptRoot=/home/site/wwwroot \
HOME=/home \
FUNCTIONS_WORKER_RUNTIME=python \
ASPNETCORE_URLS=http://+:80 \
DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_USE_POLLING_FILE_WATCHER=true

# Install Python dependencies
RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor --batch --yes -o /usr/share/keyrings/microsoft-prod.gpg && \
apt-get update && \
apt-get install -y wget apt-transport-https curl gnupg2 locales && \
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \
echo "deb [arch=amd64] https://packages.microsoft.com/debian/12/prod bookworm main" | tee /etc/apt/sources.list.d/mssql-release.list && \
# Needed for libss3 and in turn MS SQL
echo 'deb http://security.debian.org/debian-security bookworm-security main' >> /etc/apt/sources.list && \
curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list && \
# install MS SQL related packages.pinned version in PR # 1012.
echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && \
locale-gen && \
apt-get update && \
# MS SQL related packages: unixodbc msodbcsql18 mssql-tools
ACCEPT_EULA=Y apt-get install -y unixodbc msodbcsql18 mssql-tools18 && \
# OpenCV dependencies:libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb && \
# .NET Core dependencies: ca-certificates libc6 libgcc1 libgssapi-krb5-2 libicu72 libssl3 libstdc++6 zlib1g
# Azure ML dependencies: liblttng-ust0
# OpenMP dependencies: libgomp1
# binutils: binutils
apt-get install -y --no-install-recommends ca-certificates \
libc6 libgcc1 libgssapi-krb5-2 libicu72 libssl3 libstdc++6 zlib1g && \
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev xvfb binutils \
libgomp1 liblttng-ust1 && \
rm -rf /var/lib/apt/lists/*

RUN apt-get update && \
apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev libffi-dev liblzma-dev python3-openssl git unixodbc-dev dh-autoreconf \
libcurl4-openssl-dev libssl-dev python3-dev libevent-dev python3-openssl squashfs-tools unzip
2 changes: 2 additions & 0 deletions src/Cli/func/StaticResources/StaticResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static class StaticResources

public static Task<string> DockerfilePython313 => GetValue("Dockerfile.python3.13");

public static Task<string> DockerfilePython313BuildEnv => GetValue("Dockerfile.python3.13-buildenv");

public static Task<string> DockerfilePowershell7 => GetValue("Dockerfile.powershell7");

public static Task<string> DockerfilePowershell72 => GetValue("Dockerfile.powershell7.2");
Expand Down