Skip to content

Commit 0f3a0dc

Browse files
authored
Merge pull request #264 from jinyan-li1/py312-upgrade-clean
Upgrade scikit-learn container to Python 3.12
2 parents 5efefe7 + f30e250 commit 0f3a0dc

15 files changed

Lines changed: 214 additions & 211 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ src/sagemaker_sklearn_container.egg-info/
77
__pycache__
88
.coverage*
99
.mypy_cache/
10+
Dockerfile.test
11+
test.parquet
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
ARG UBUNTU_VERSION=24.04
2+
ARG UBUNTU_IMAGE_DIGEST=b359f1067efa76f37863778f7b6d0e8d911e3ee8efa807ad01fbf5dc1ef9006b
3+
4+
# Build stage for SQLite compilation
5+
FROM ubuntu:${UBUNTU_VERSION}@sha256:${UBUNTU_IMAGE_DIGEST} as sqlite-builder
6+
RUN apt-get update && apt-get install -y --no-install-recommends \
7+
build-essential wget ca-certificates && \
8+
cd /tmp && \
9+
wget https://www.sqlite.org/2025/sqlite-autoconf-3500200.tar.gz && \
10+
tar xzf sqlite-autoconf-3500200.tar.gz && \
11+
cd sqlite-autoconf-3500200 && \
12+
./configure --prefix=/usr/local && \
13+
make && \
14+
make install && \
15+
ldconfig && \
16+
cd / && \
17+
rm -rf /tmp/sqlite-autoconf-3500200 /tmp/sqlite-autoconf-3500200.tar.gz && \
18+
apt-get clean && \
19+
rm -rf /var/lib/apt/lists/*
20+
21+
# Main image
22+
FROM ubuntu:${UBUNTU_VERSION}@sha256:${UBUNTU_IMAGE_DIGEST}
23+
24+
ARG PYTHON_VERSION=3.12
25+
26+
ENV DEBIAN_FRONTEND=noninteractive
27+
28+
# Install runtime dependencies only
29+
RUN apt-get update && \
30+
apt-get -y upgrade && \
31+
apt-get -y install --no-install-recommends \
32+
curl git jq libatlas-base-dev nginx openjdk-8-jdk-headless unzip wget expat tzdata apparmor \
33+
libgstreamer1.0-0 libxml2 libsqlite3-0 software-properties-common ca-certificates lsb-release \
34+
build-essential linux-libc-dev && \
35+
# Add Apache Arrow repository for runtime libraries only
36+
wget https://packages.apache.org/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb && \
37+
apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb && \
38+
apt-get update && \
39+
apt-get install -y -V libarrow-dev=17.0.0-1 libarrow-dataset-dev=17.0.0-1 libparquet-dev=17.0.0-1 libarrow-acero-dev=17.0.0-1 && \
40+
# Add deadsnakes PPA for Python 3.12
41+
add-apt-repository ppa:deadsnakes/ppa && \
42+
apt-get update && \
43+
apt-get -y install --no-install-recommends \
44+
python3.12 python3.12-dev && \
45+
update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1 && \
46+
# Create python symlink for backward compatibility
47+
ln -sf /usr/bin/python3 /usr/bin/python && \
48+
curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --break-system-packages && \
49+
apt-get clean && \
50+
rm -rf /var/lib/apt/lists/*
51+
52+
RUN ln -fs /usr/share/zoneinfo/UTC /etc/localtime && \
53+
dpkg-reconfigure --frontend noninteractive tzdata
54+
55+
# Install uv for fast Python package management
56+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
57+
mv /root/.local/bin/uv /usr/local/bin/uv
58+
59+
ENV PATH=/usr/local/bin:${PATH}
60+
ENV PIP_ROOT_USER_ACTION=ignore
61+
62+
# Copy compiled SQLite from builder stage
63+
COPY --from=sqlite-builder /usr/local/bin/sqlite3 /usr/local/bin/sqlite3
64+
COPY --from=sqlite-builder /usr/local/lib/libsqlite3.* /usr/local/lib/
65+
COPY --from=sqlite-builder /usr/local/include/sqlite3*.h /usr/local/include/
66+
67+
# Update library cache and ensure /usr/local/bin is in PATH
68+
RUN ldconfig && \
69+
echo "/usr/local/lib" > /etc/ld.so.conf.d/sqlite3.conf && \
70+
ldconfig
71+
72+
ENV PATH="/usr/local/bin:${PATH}"
73+
74+
# This command will check the version and print it to the build logs
75+
RUN sqlite3 --version
76+
77+
# Install awscli
78+
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
79+
unzip awscliv2.zip && \
80+
./aws/install && \
81+
rm -r aws awscliv2.zip
82+
83+
# Python won't try to write .pyc or .pyo files on the import of source modules
84+
# Force stdin, stdout and stderr to be totally unbuffered. Good for logging
85+
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PYTHONIOENCODING=UTF-8 LANG=C.UTF-8 LC_ALL=C.UTF-8
86+
87+
# Install core scientific packages with exact versions
88+
RUN uv pip install --system --no-cache --break-system-packages \
89+
numpy==2.1.0 \
90+
scikit-learn==1.4.2 \
91+
pyarrow==17.0.0

docker/1.4-2/extension/Dockerfile.cpu renamed to docker/1.4-2-py312/extension/Dockerfile.cpu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM preprod-sklearn:1.4-2
1+
FROM preprod-sklearn:1.4-2-py312
22

33
RUN pip freeze | grep -q 'scikit-learn==1.4.2'; \
44
if [ $? -eq 0 ]; \
File renamed without changes.
Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
1-
FROM sklearn-base:1.4-2
2-
ENV SAGEMAKER_SKLEARN_VERSION 1.4-2
1+
FROM sklearn-base:1.4-2-py312
2+
ENV SAGEMAKER_SKLEARN_VERSION 1.4-2-py312
33
ENV PIP_ROOT_USER_ACTION=ignore
44

55
LABEL com.amazonaws.sagemaker.capabilities.accept-bind-to-port=true
66

77
# Install remaining packages via pip
88
COPY requirements.txt /requirements.txt
9-
RUN uv pip install --system -r /requirements.txt && \
9+
RUN uv pip install --system --break-system-packages -r /requirements.txt && \
1010
rm /requirements.txt
1111

12-
# Fix Python 3.10 compatibility for sagemaker-containers
13-
RUN python3 -c "import sys; import os; site_packages = '/usr/local/lib/python3.10/dist-packages'; mapping_file = os.path.join(site_packages, 'sagemaker_containers/_mapping.py'); exec('if os.path.exists(mapping_file):\\n with open(mapping_file, \"r\") as f:\\n content = f.read()\\n content = content.replace(\"collections.Mapping\", \"collections.abc.Mapping\")\\n with open(mapping_file, \"w\") as f:\\n f.write(content)')"
12+
# Fix Python 3.12 compatibility for sagemaker-containers
13+
RUN python3 -c "import sys; import os; site_packages = '/usr/local/lib/python3.12/dist-packages'; mapping_file = os.path.join(site_packages, 'sagemaker_containers/_mapping.py'); exec('if os.path.exists(mapping_file):\\n with open(mapping_file, \"r\") as f:\\n content = f.read()\\n content = content.replace(\"collections.Mapping\", \"collections.abc.Mapping\")\\n with open(mapping_file, \"w\") as f:\\n f.write(content)')"
1414

1515
COPY dist/sagemaker_sklearn_container-2.0-py3-none-any.whl /sagemaker_sklearn_container-2.0-py3-none-any.whl
16-
RUN uv pip install --system --no-cache /sagemaker_sklearn_container-2.0-py3-none-any.whl && \
16+
RUN uv pip install --system --no-cache --break-system-packages /sagemaker_sklearn_container-2.0-py3-none-any.whl && \
1717
rm /sagemaker_sklearn_container-2.0-py3-none-any.whl
1818

19+
# Force upgrade six to 1.16.0 after all packages are installed to ensure six.moves compatibility
20+
RUN uv pip install --system --break-system-packages --upgrade --force-reinstall six==1.16.0
21+
1922
# CWE-502: Patch sagemaker-inference decoder to disable pickle deserialization
2023
COPY docker/$SAGEMAKER_SKLEARN_VERSION/resources/patches/decoder.py \
21-
/usr/local/lib/python3.10/dist-packages/sagemaker_inference/decoder.py
24+
/usr/local/lib/python3.12/dist-packages/sagemaker_inference/decoder.py
25+
26+
# Configure pip to always use --break-system-packages for Python 3.12
27+
RUN mkdir -p /root/.config/pip && \
28+
echo "[global]" > /root/.config/pip/pip.conf && \
29+
echo "break-system-packages = true" >> /root/.config/pip/pip.conf
2230

2331
ENV SAGEMAKER_TRAINING_MODULE sagemaker_sklearn_container.training:main
2432
ENV SAGEMAKER_SERVING_MODULE sagemaker_sklearn_container.serving:main
@@ -60,4 +68,4 @@ EXPOSE 8080
6068
ENV TEMP=/home/model-server/tmp
6169

6270
# Required label for multi-model loading
63-
LABEL com.amazonaws.sagemaker.capabilities.multi-models=true
71+
LABEL com.amazonaws.sagemaker.capabilities.multi-models=true
File renamed without changes.

docker/1.4-2/resources/mms/ExecutionParameters.java renamed to docker/1.4-2-py312/resources/mms/ExecutionParameters.java

File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Copyright 2019-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the 'License'). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the 'license' file accompanying this file. This file is
10+
# distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""This module contains functionality for converting various types of
14+
files and objects to NumPy arrays."""
15+
from __future__ import absolute_import
16+
17+
import json
18+
from typing import Iterable # noqa ignore=F401 imported but unused
19+
20+
import numpy as np
21+
from six import BytesIO, StringIO
22+
23+
from sagemaker_inference import content_types, errors
24+
25+
26+
def _json_to_numpy(string_like, dtype=None): # type: (str) -> np.array
27+
"""Convert a JSON object to a numpy array.
28+
29+
Args:
30+
string_like (str): JSON string.
31+
dtype (dtype, optional): Data type of the resulting array.
32+
If None, the dtypes will be determined by the contents
33+
of each column, individually. This argument can only be
34+
used to 'upcast' the array. For downcasting, use the
35+
.astype(t) method.
36+
37+
Returns:
38+
(np.array): numpy array
39+
"""
40+
data = json.loads(string_like)
41+
return np.array(data, dtype=dtype)
42+
43+
44+
def _csv_to_numpy(string_like, dtype=None): # type: (str) -> np.array
45+
"""Convert a CSV object to a numpy array.
46+
47+
Args:
48+
string_like (str): CSV string.
49+
dtype (dtype, optional): Data type of the resulting array. If None,
50+
the dtypes will be determined by the contents of each column,
51+
individually. This argument can only be used to 'upcast' the array.
52+
For downcasting, use the .astype(t) method.
53+
54+
Returns:
55+
(np.array): numpy array
56+
"""
57+
stream = StringIO(string_like)
58+
return np.genfromtxt(stream, dtype=dtype, delimiter=",")
59+
60+
61+
def _npy_to_numpy(npy_array): # type: (object) -> np.array
62+
"""Convert a NPY array into numpy.
63+
64+
Args:
65+
npy_array (npy array): to be converted to numpy array
66+
67+
Returns:
68+
(np.array): converted numpy array.
69+
"""
70+
stream = BytesIO(npy_array)
71+
return np.load(stream, allow_pickle=False)
72+
73+
74+
_decoder_map = {
75+
content_types.NPY: _npy_to_numpy,
76+
content_types.CSV: _csv_to_numpy,
77+
content_types.JSON: _json_to_numpy,
78+
}
79+
80+
81+
def decode(obj, content_type):
82+
# type: (np.array or Iterable or int or float, str) -> np.array
83+
"""Decode an object to one of the default content types to a numpy array.
84+
85+
Args:
86+
obj (object): to be decoded.
87+
content_type (str): content type to be used.
88+
89+
Returns:
90+
np.array: decoded object.
91+
"""
92+
try:
93+
decoder = _decoder_map[content_type]
94+
return decoder(obj)
95+
except KeyError:
96+
raise errors.UnsupportedFormatError(content_type)

0 commit comments

Comments
 (0)