Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1e9da23
Support Python 3.13
fangchenli Dec 3, 2025
f8d0254
Add Python 3.13 support and GitHub Actions workflow
fangchenli Dec 4, 2025
5aa1324
Merge remote-tracking branch 'upstream' into add-python-3.13-support
fangchenli Dec 4, 2025
d37c924
Use bazelisk instead of bazel in GitHub Actions
fangchenli Dec 4, 2025
d2299ad
Upgrade opencv-contrib-python to 4.12.0.88 and protobuf to 5.29.5
fangchenli Dec 4, 2025
6f20158
Fix CI test imports by changing directory before testing
fangchenli Dec 4, 2025
b593a9e
Simplify CI workflow: use lock files directly
fangchenli Dec 4, 2025
2087954
Merge remote-tracking branch 'upstream' into add-python-3.13-support
fangchenli Dec 4, 2025
e7f7998
Fix lock file naming convention and add Python 3.9 lock file
fangchenli Dec 4, 2025
18adee5
Improve Homebrew caching to fix protobuf linking issue
fangchenli Dec 5, 2025
ae48b1c
Set default __version__ to 'dev' in setup.py
fangchenli Dec 5, 2025
2afab9e
Remove old requirements_lock.txt file
fangchenli Dec 5, 2025
6d99417
Update lockfiles
fangchenli Dec 5, 2025
4949682
Update WORKSPACE and BUILD to use requirements_lock_3_9.txt
fangchenli Dec 5, 2025
06cfcc1
Pin sentencepiece>=0.2.1 for Python 3.13 and regenerate lock file
fangchenli Dec 5, 2025
c656cb7
remove py39 from CI, bump sentencepiece version for py313
fangchenli Dec 5, 2025
a65bddb
Merge remote-tracking branch 'upstream' into add-python-3.13-support
fangchenli Dec 5, 2025
8b92f9c
Revert OpenCV build-from-source configuration changes
fangchenli Dec 5, 2025
e72329c
Revert OpenCV source version from 4.10.0 to 3.4.11
fangchenli Dec 5, 2025
8bd9e50
fix branch name
fangchenli Dec 5, 2025
96e5644
Merge remote-tracking branch 'upstream' into add-python-3.13-support
fangchenli Dec 5, 2025
01a1c31
test python 3.9
fangchenli Dec 5, 2025
7b9f3f3
Merge branch 'master' into add-python-3.13-support
fangchenli Dec 14, 2025
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
182 changes: 182 additions & 0 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
name: Build Python Wheels

on:
push:
branches:
- master
- add-python-3.13-support
tags:
- 'v*'
pull_request:
branches:
- master
- add-python-3.13-support
workflow_dispatch:

env:
BAZEL_VERSION: '7.4.1'
PROTOBUF_VERSION: '29'

jobs:
build-macos-arm64:
name: Build macOS ARM64 wheel (Python ${{ matrix.python-version }})
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- name: Cache Bazel
uses: actions/cache@v4
with:
path: |
~/.cache/bazel
~/.cache/bazelisk
key: ${{ runner.os }}-bazel-${{ env.BAZEL_VERSION }}-py${{ matrix.python-version }}-${{ hashFiles('.bazelversion', 'WORKSPACE', '**/*.bzl') }}
restore-keys: |
${{ runner.os }}-bazel-${{ env.BAZEL_VERSION }}-py${{ matrix.python-version }}-
${{ runner.os }}-bazel-${{ env.BAZEL_VERSION }}-
${{ runner.os }}-bazel-

- name: Cache Homebrew packages
uses: actions/cache@v4
with:
path: |
~/Library/Caches/Homebrew/bazelisk--*
~/Library/Caches/Homebrew/opencv--*
~/Library/Caches/Homebrew/protobuf@${{ env.PROTOBUF_VERSION }}--*
~/Library/Caches/Homebrew/downloads/*--bazelisk-*
~/Library/Caches/Homebrew/downloads/*--opencv-*
~/Library/Caches/Homebrew/downloads/*--protobuf@${{ env.PROTOBUF_VERSION }}-*
key: ${{ runner.os }}-brew-${{ hashFiles('.github/workflows/build-wheels-py313.yml') }}
restore-keys: |
${{ runner.os }}-brew-

- name: Install system dependencies
run: |
# Disable auto-update for faster installs
export HOMEBREW_NO_AUTO_UPDATE=1

# Use bazelisk instead of bazel for automatic version management
brew install bazelisk opencv protobuf@${{ env.PROTOBUF_VERSION }}

- name: Verify system dependencies
id: verify-deps
run: |
echo "=== Bazel ==="
bazel --version

echo "=== OpenCV ==="
OPENCV_VERSION=$(brew ls opencv | grep version.hpp | head -1 | sed -E 's|.*/opencv/([^/]+)/.*|\1|')
echo "version=$OPENCV_VERSION" >> $GITHUB_OUTPUT
echo "OpenCV version: $OPENCV_VERSION"

OPENCV_MAJOR_MINOR=$(echo $OPENCV_VERSION | sed -E 's/([0-9]+\.[0-9]+).*/\1/')
echo "major_minor=$OPENCV_MAJOR_MINOR" >> $GITHUB_OUTPUT
echo "OpenCV major.minor: $OPENCV_MAJOR_MINOR"

echo "=== Protobuf ==="
PROTOC_PATH=/opt/homebrew/opt/protobuf@${{ env.PROTOBUF_VERSION }}/bin/protoc
echo "path=$PROTOC_PATH" >> $GITHUB_OUTPUT
$PROTOC_PATH --version

echo "=== Python ==="
python --version
pip --version

- name: Set version
id: set-version
run: |
# Generate version from git commit and date
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
# If triggered by a tag, use the tag name (strip 'v' prefix)
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
else
# Otherwise, use date-based version with short commit hash
VERSION="0.10.14.dev$(date +%Y%m%d)+g$(git rev-parse --short HEAD)"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using version: $VERSION"

- name: Configure build environment
run: |
# Update OpenCV configuration
sed -i '' "s|PREFIX = \"opencv/.*\"|PREFIX = \"opencv/${{ steps.verify-deps.outputs.version }}\"|" third_party/opencv_macos.BUILD
sed -i '' "s|OPENCV_SO_VERSION = \".*\"|OPENCV_SO_VERSION = \"${{ steps.verify-deps.outputs.major_minor }}\"|" third_party/BUILD

# Update version in setup.py
sed -i '' "s|__version__ = '.*'|__version__ = '${{ steps.set-version.outputs.version }}'|" setup.py

echo "Configuration updated:"
grep "PREFIX = " third_party/opencv_macos.BUILD
grep "OPENCV_SO_VERSION = " third_party/BUILD
grep "__version__ = " setup.py

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
# Convert python-version from "3.9" to "3_9" format for lock file
LOCK_VERSION=$(echo "${{ matrix.python-version }}" | tr '.' '_')
pip install -r "requirements_lock_${LOCK_VERSION}.txt"

- name: Build wheel
env:
HERMETIC_PYTHON_VERSION: ${{ matrix.python-version }}
PROTOC: ${{ steps.verify-deps.outputs.path }}
run: |
echo "::group::Build MediaPipe wheel"
python setup.py bdist_wheel 2>&1 | tee build.log
echo "::endgroup::"

- name: Verify wheel
run: |
ls -lh dist/
file dist/*.whl
unzip -l dist/*.whl | head -50

- name: Test wheel installation
run: |
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
pip install dist/*.whl

# Change to a different directory to avoid importing from source
cd /tmp
echo "::group::Test imports"
python -c "import mediapipe; print(f'MediaPipe version: {mediapipe.__version__}')"
python -c "import mediapipe.tasks.python.vision as vision; print('Vision tasks: OK')"
python -c "import mediapipe.tasks.python.text as text; print('Text tasks: OK')"
python -c "import mediapipe.tasks.python.audio as audio; print('Audio tasks: OK')"
echo "::endgroup::"

deactivate

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-macos-arm64-py${{ matrix.python-version }}
path: dist/*.whl
retention-days: 90
if-no-files-found: error

- name: Upload build log
if: always()
uses: actions/upload-artifact@v4
with:
name: build-log-macos-arm64-py${{ matrix.python-version }}
path: build.log
retention-days: 30
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,20 @@ mediapipe/provisioning_profile.mobileprovision
node_modules/
.configure.bazelrc
.user.bazelrc

# Python build artifacts
build/
dist/
*.egg-info/
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
venv/
venv_py*/
*.so
*.dylib
mediapipe/__init__.py.backup
__init__.py
build.log
21 changes: 8 additions & 13 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,9 @@ http_archive(
http_archive(
name = "zlib",
build_file = "@//third_party:zlib.BUILD",
patch_args = [
"-p1",
],
patches = [
"@//third_party:zlib.diff",
],
sha256 = "b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30",
strip_prefix = "zlib-1.2.13",
url = "http://zlib.net/fossils/zlib-1.2.13.tar.gz",
sha256 = "9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23",
strip_prefix = "zlib-1.3.1",
url = "https://zlib.net/fossils/zlib-1.3.1.tar.gz",
)

# gflags needed by glog
Expand Down Expand Up @@ -364,10 +358,11 @@ python_init_repositories(
local_wheel_inclusion_list = ["mediapipe*"],
local_wheel_workspaces = ["//:WORKSPACE"],
requirements = {
"3.9": "//:requirements_lock.txt",
"3.9": "//:requirements_lock_3_9.txt",
"3.10": "//:requirements_lock_3_10.txt",
"3.11": "//:requirements_lock_3_11.txt",
"3.12": "//:requirements_lock_3_12.txt",
"3.13": "//:requirements_lock_3_13.txt",
},
)

Expand Down Expand Up @@ -400,7 +395,7 @@ load("@rules_python//python:pip.bzl", "pip_parse")

pip_parse(
name = "mediapipe_pip_deps",
requirements_lock = "@//:requirements_lock.txt",
requirements_lock = "@//:requirements_lock_3_9.txt",
)

load("@mediapipe_pip_deps//:requirements.bzl", mp_install_deps = "install_deps")
Expand Down Expand Up @@ -606,10 +601,10 @@ new_local_repository(
new_local_repository(
name = "macos_opencv",
build_file = "@//third_party:opencv_macos.BUILD",
# For local MacOS builds, the path should point to an opencv@3 installation.
# For local MacOS builds, the path should point to an opencv installation.
# If you edit the path here, you will also need to update the corresponding
# prefix in "opencv_macos.BUILD".
path = "/usr/local", # e.g. /usr/local/Cellar for HomeBrew
path = "/opt/homebrew/Cellar",
)

new_local_repository(
Expand Down
4 changes: 3 additions & 1 deletion mediapipe/python/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pybind_extension(
"-lopencv_calib3d",
"-lopencv_imgcodecs",
],
}) + select({
"@bazel_tools//src/conditions:darwin": ["-undefined", "dynamic_lookup"],
"//conditions:default": [],
}),
module_name = "_framework_bindings",
deps = [
Expand All @@ -48,7 +51,6 @@ pybind_extension(
"//mediapipe/python/pybind:resource_util",
"//mediapipe/python/pybind:timestamp",
"//mediapipe/python/pybind:validated_graph_config",
"//mediapipe/tasks/python/core/pybind:task_runner",
"@com_google_absl//absl/strings:str_format",
"@stblib//:stb_image",
# Type registration.
Expand Down
2 changes: 1 addition & 1 deletion mediapipe/python/pybind/calculator_graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ void CalculatorGraphSubmodule(pybind11::module* module) {
RaisePyErrorIfNotOk(self->ObserveOutputStream(
stream_name,
[callback_fn, stream_name](const Packet& packet) {
absl::MutexLock lock(callback_mutex);
absl::MutexLock lock(&callback_mutex);
// Acquires GIL before calling Python callback.
py::gil_scoped_acquire gil_acquire;
callback_fn(stream_name, packet);
Expand Down
4 changes: 4 additions & 0 deletions mediapipe/tasks/cc/metadata/python/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ pybind_extension(
"metadata_version.cc",
],
features = ["-use_header_modules"],
linkopts = select({
"@bazel_tools//src/conditions:darwin": ["-undefined", "dynamic_lookup"],
"//conditions:default": [],
}),
module_name = "_pywrap_metadata_version",
deps = [
"//mediapipe/tasks/cc/metadata:metadata_version",
Expand Down
4 changes: 3 additions & 1 deletion mediapipe/tasks/python/text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import mediapipe.tasks.python.text.language_detector
import mediapipe.tasks.python.text.text_classifier
import mediapipe.tasks.python.text.text_embedder
from mediapipe.tasks.python.components.containers import embedding_result

LanguageDetector = language_detector.LanguageDetector
LanguageDetectorOptions = language_detector.LanguageDetectorOptions
Expand All @@ -26,10 +27,11 @@
TextClassifierResult = text_classifier.TextClassifierResult
TextEmbedder = text_embedder.TextEmbedder
TextEmbedderOptions = text_embedder.TextEmbedderOptions
TextEmbedderResult = text_embedder.TextEmbedderResult
TextEmbedderResult = embedding_result.EmbeddingResult

# Remove unnecessary modules to avoid duplication in API docs.
del mediapipe
del language_detector
del text_classifier
del text_embedder
del embedding_result
4 changes: 3 additions & 1 deletion mediapipe/tasks/python/vision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import mediapipe.tasks.python.vision.face_detector
import mediapipe.tasks.python.vision.face_landmarker
import mediapipe.tasks.python.vision.gesture_recognizer
import mediapipe.tasks.python.vision.gesture_recognizer_result
import mediapipe.tasks.python.vision.hand_landmarker
import mediapipe.tasks.python.vision.image_classifier
import mediapipe.tasks.python.vision.image_embedder
Expand All @@ -35,7 +36,7 @@
FaceLandmarksConnections = face_landmarker.FaceLandmarksConnections
GestureRecognizer = gesture_recognizer.GestureRecognizer
GestureRecognizerOptions = gesture_recognizer.GestureRecognizerOptions
GestureRecognizerResult = gesture_recognizer.GestureRecognizerResult
GestureRecognizerResult = gesture_recognizer_result.GestureRecognizerResult
HandLandmarker = hand_landmarker.HandLandmarker
HandLandmarkerOptions = hand_landmarker.HandLandmarkerOptions
HandLandmarkerResult = hand_landmarker.HandLandmarkerResult
Expand Down Expand Up @@ -67,6 +68,7 @@
del face_detector
del face_landmarker
del gesture_recognizer
del gesture_recognizer_result
del hand_landmarker
del image_classifier
del image_embedder
Expand Down
18 changes: 14 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
absl-py~=2.3
numpy
sounddevice~=0.5
flatbuffers~=25.9
absl-py
attrs>=19.1.0
flatbuffers>=2.0
jax
jaxlib
matplotlib
numpy<2; python_version < "3.13"
numpy; python_version >= "3.13"
opencv-contrib-python<4.12; python_version < "3.13"
opencv-contrib-python; python_version >= "3.13"
protobuf>=5.29.3,<6
sounddevice>=0.4.4
sentencepiece>=0.2.1; python_version >= "3.13"
sentencepiece; python_version < "3.13"
Loading