From d7ddd16341cb2676c8b4d5122c458d4af7a62d7d Mon Sep 17 00:00:00 2001 From: Keming Date: Wed, 8 Jan 2025 12:17:48 +0800 Subject: [PATCH 1/4] feat: adopt maturin Signed-off-by: Keming --- .github/workflows/package.yml | 152 ++++++++++++++++++++++++---------- Cargo.lock | 2 +- Cargo.toml | 2 +- MANIFEST.in | 11 --- Makefile | 15 +--- mosec/runtime.py | 4 +- pyproject.toml | 20 ++--- requirements/dev.txt | 1 + 8 files changed, 126 insertions(+), 81 deletions(-) delete mode 100644 MANIFEST.in diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 45eb31f3..d0250574 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -1,75 +1,143 @@ +# This file added mautrin autogenerated ci file by maturin v1.8.1 +# DO NOT OVERWRITE THIS FILE by `maturin generate-ci github` directly + name: PyPI Publish on: release: types: [created] + workflow_dispatch: concurrency: group: ${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true +permissions: + contents: read + jobs: - build: - name: "Build PyPI Package" - runs-on: ${{ matrix.os }} - timeout-minutes: 60 + linux: + runs-on: ${{ matrix.platform.runner }} strategy: matrix: - os: [ubuntu-latest, macos-13, macos-14] + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + - runner: ubuntu-22.04 + target: s390x + - runner: ubuntu-22.04 + target: ppc64le + steps: + - uses: actions/checkout@v4 + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist + sccache: 'true' + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-linux-${{ matrix.platform.target }} + path: dist + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 steps: - uses: actions/checkout@v4 + - name: Build wheels + uses: PyO3/maturin-action@v1 with: - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v5 + target: ${{ matrix.platform.target }} + args: --release --out dist + sccache: 'true' + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v4 with: - python-version: 3.11 - - name: Build - run: | - python -m pip install pipx - pipx run cibuildwheel + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-13 + target: x86_64 + - runner: macos-14 + target: aarch64 + steps: + - uses: actions/checkout@v4 + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist + sccache: 'true' - name: Upload wheels uses: actions/upload-artifact@v4 with: - name: wheels_${{ github.event.release.tag_name }}_${{ matrix.os }} - retention-days: 1 - path: ./wheelhouse/*.whl + name: wheels-macos-${{ matrix.platform.target }} + path: dist - publish: - name: "Publish PyPI Package" + sdist: runs-on: ubuntu-latest - permissions: - id-token: write - needs: [build] steps: - uses: actions/checkout@v4 + - name: Build sdist + uses: PyO3/maturin-action@v1 with: - fetch-depth: 0 - - uses: actions/setup-python@v5 + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v4 with: - python-version: 3.9 + name: wheels-sdist + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} + needs: [linux, musllinux, macos, sdist] + permissions: + # Use to sign the release artifacts + id-token: write + # Used to upload release artifacts + contents: write + # Used to generate artifact attestation + attestations: write + steps: - uses: actions/download-artifact@v4 + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 with: - pattern: wheels_${{ github.event.release.tag_name }}_* - merge-multiple: true - path: dist/ - - name: Prepare PyPI package - run: | - python -m pip install --upgrade pip - pip install setuptools wheel - python setup.py sdist - ls dist/ - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + subject-path: 'wheels-*/*' + - name: Publish to PyPI + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: PyO3/maturin-action@v1 with: - skip-existing: true - - name: Publish Crates - env: - CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }} - run: | - cargo login $CRATES_TOKEN - cargo publish + command: upload + args: --non-interactive --skip-existing wheels-*/* image: name: "Build Docker Image" diff --git a/Cargo.lock b/Cargo.lock index d4999e0e..6685ca19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "mosec" -version = "0.9.0" +version = "0.9.1" dependencies = [ "async-channel", "async-stream", diff --git a/Cargo.toml b/Cargo.toml index 60605558..3da466f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mosec" -version = "0.9.0" +version = "0.9.1" authors = ["Keming ", "Zichen "] edition = "2021" license = "Apache-2.0" diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7254fff2..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,11 +0,0 @@ -include Cargo.* -include src/*.rs -include CITATION.cff -include LICENSE -include license.json -include README.md -include mosec/py.typed - -prune tests -prune examples -prune .github diff --git a/Makefile b/Makefile index 69ddf09a..d4c7aabf 100644 --- a/Makefile +++ b/Makefile @@ -4,16 +4,13 @@ PY_SOURCE_FILES=mosec tests examples setup.py RUST_SOURCE_FILES=src/* install: - pip install -e .[dev,doc,mixin] + pip install -r requirements/dev.txt -r requirements/mixin.txt -r requirements/doc.txt pre-commit install rustup toolchain install nightly rustup component add rustfmt --toolchain nightly dev: - cargo build - @mkdir -p mosec/bin - @cp ./target/debug/mosec mosec/bin/mosec - pip install -e .[dev] + maturin develop test: dev @pip install -q -r requirements/mixin.txt @@ -48,15 +45,11 @@ doc: clean: @cargo clean @-rm -rf build/ dist/ .eggs/ site/ *.egg-info .pytest_cache .mypy_cache .ruff_cache - @-rm -rf mosec/bin/ @-find . -name '*.pyc' -type f -exec rm -rf {} + @-find . -name '__pycache__' -exec rm -rf {} + package: clean - PRODUCTION_MODE=yes python setup.py bdist_wheel - -cross_compile: clean - cibuildwheel --platform linux + maturin build --release --out dist publish: package twine upload dist/* @@ -78,7 +71,7 @@ semantic_lint: @cargo clippy -- -D warnings version: - @python -m setuptools_scm + @cargo metadata --format-version 1 | jq -r '.packages[] | select(.name == "mosec") | .version' add_license: @addlicense -c "MOSEC Authors" **/*.py **/*.rs **/**/*.py diff --git a/mosec/runtime.py b/mosec/runtime.py index 55246123..60b31fa7 100644 --- a/mosec/runtime.py +++ b/mosec/runtime.py @@ -16,7 +16,6 @@ import multiprocessing as mp import subprocess -from importlib.resources import files as importlib_files from multiprocessing.context import ForkContext, SpawnContext from multiprocessing.process import BaseProcess from multiprocessing.synchronize import Event @@ -238,7 +237,6 @@ def __init__(self, timeout: int): """ self.process: Optional[subprocess.Popen] = None - self.server_path = importlib_files("mosec") / "bin" / "mosec" self.timeout = timeout def halt(self): @@ -272,5 +270,5 @@ def start(self, config_path: Path) -> subprocess.Popen: """ # pylint: disable=consider-using-with - self.process = subprocess.Popen([str(self.server_path), config_path]) + self.process = subprocess.Popen([str("mosec"), config_path]) return self.process diff --git a/pyproject.toml b/pyproject.toml index 859f1dc7..13646d94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Rust", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Software Development :: Libraries :: Python Modules", @@ -35,22 +36,17 @@ homepage = "https://mosecorg.github.io/" documentation = "https://mosecorg.github.io/mosec/" repository = "https://github.com/mosecorg/mosec" changelog = "https://github.com/mosecorg/mosec/releases" - -[tool.cibuildwheel] -build-frontend = "build" -skip = ["cp36-*", "cp37-*", "cp38-*", "*-musllinux_*", "pp*"] -archs = ["auto64"] -before-all = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y" -environment = { PRODUCTION_MODE="yes", PATH="$PATH:$HOME/.cargo/bin", PIP_NO_CLEAN="yes" } -before-build = "git status" # help to debug what happened to the setuptools_scm (file changes) - [project.scripts] [build-system] -requires = ["setuptools>=45", "wheel", "setuptools_scm>=7.0"] +requires = ["maturin>=1.8,<2.0"] +build-backend = "maturin" -[tool.setuptools_scm] -write_to = "mosec/_version.py" +[tool.maturin] +bindings = "bin" +python-packages = ["mosec"] +strip = true +exclude = ["tests/**/*", "docs/**/*", "examples/**/*"] [tool.mypy] python_version = "3.9" diff --git a/requirements/dev.txt b/requirements/dev.txt index e2066a6a..e88f645f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -8,3 +8,4 @@ pre-commit>=2.15.0 httpx[http2]==0.28.1 httpx-sse==0.4.0 zstandard~=0.23 +maturin>=1.8,<2.0 From 58efc12a12af21a0e49a7df44195d3917dfe1f7e Mon Sep 17 00:00:00 2001 From: Keming Date: Wed, 8 Jan 2025 12:49:10 +0800 Subject: [PATCH 2/4] fix ci install dependencies Signed-off-by: Keming --- .github/workflows/check.yml | 7 +------ .github/workflows/nightly.yml | 5 +---- .github/workflows/page.yml | 5 +---- Makefile | 2 +- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 523a4647..ea1f95c5 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -39,8 +39,6 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: 3.9 @@ -69,8 +67,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -82,8 +78,7 @@ jobs: toolchain: nightly - name: Install components run: | - python -m pip install --upgrade pip - rustup component add clippy + make install - name: Test unit run: | make test_unit diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7dad7529..74fd4083 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -20,16 +20,13 @@ jobs: steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -e .[dev,mixin] + make install make dev - name: Test run: | diff --git a/.github/workflows/page.yml b/.github/workflows/page.yml index 54f2e8ab..cd3edba6 100644 --- a/.github/workflows/page.yml +++ b/.github/workflows/page.yml @@ -30,8 +30,6 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Setup Pages uses: actions/configure-pages@v5 - name: Set up Python @@ -40,8 +38,7 @@ jobs: python-version: 3.9 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -e .[dev,doc,mixin] + make install - name: Generate docs run: | cd docs && make html diff --git a/Makefile b/Makefile index d4c7aabf..e906cd3c 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ install: pip install -r requirements/dev.txt -r requirements/mixin.txt -r requirements/doc.txt pre-commit install rustup toolchain install nightly - rustup component add rustfmt --toolchain nightly + rustup component add rustfmt clippy --toolchain nightly dev: maturin develop From 6889e1a1506714e95595b8943360fdbc60416b3a Mon Sep 17 00:00:00 2001 From: Keming Date: Wed, 8 Jan 2025 13:09:29 +0800 Subject: [PATCH 3/4] fix ci install editable Signed-off-by: Keming --- Cargo.lock | 12 ++++++------ Makefile | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6685ca19..46235b2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,9 +332,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -895,9 +895,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1085,9 +1085,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", diff --git a/Makefile b/Makefile index e906cd3c..fc8d82e8 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ install: rustup component add rustfmt clippy --toolchain nightly dev: - maturin develop + pip install -e . test: dev @pip install -q -r requirements/mixin.txt From 00c0c16220206bbc082c9fd806a91f9729269467 Mon Sep 17 00:00:00 2001 From: Keming Date: Thu, 9 Jan 2025 11:05:57 +0800 Subject: [PATCH 4/4] rm all setuptools related code Signed-off-by: Keming --- Makefile | 2 +- mosec/__init__.py | 7 --- pyproject.toml | 7 ++- requirements/dev.txt | 1 - setup.py | 99 ------------------------------------------- tests/test_service.py | 4 +- 6 files changed, 9 insertions(+), 111 deletions(-) delete mode 100644 setup.py diff --git a/Makefile b/Makefile index fc8d82e8..7fe2545d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL:=dev -PY_SOURCE_FILES=mosec tests examples setup.py +PY_SOURCE_FILES=mosec tests examples RUST_SOURCE_FILES=src/* install: diff --git a/mosec/__init__.py b/mosec/__init__.py index 5e9bd00c..07ef88c1 100644 --- a/mosec/__init__.py +++ b/mosec/__init__.py @@ -26,13 +26,6 @@ from mosec.server import Server from mosec.worker import SSEWorker, Worker -try: - from mosec._version import __version__ # type: ignore -except ImportError: - from setuptools_scm import get_version # type: ignore - - __version__ = get_version(root="..", relative_to=__file__) - __all__ = [ "ClientError", "DecodingError", diff --git a/pyproject.toml b/pyproject.toml index 13646d94..d727ba09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [ ] license = {text = "Apache-2.0"} keywords = ["machine learning", "deep learning", "model serving"] -dynamic = ["version", "optional-dependencies"] +dynamic = ["version"] requires-python = ">=3.9" dependencies = [] classifiers = [ @@ -37,6 +37,11 @@ documentation = "https://mosecorg.github.io/mosec/" repository = "https://github.com/mosecorg/mosec" changelog = "https://github.com/mosecorg/mosec/releases" [project.scripts] +[project.optional-dependencies] +validation = ["msgspec"] +redis = ["redis"] +msgpack = ["msgpack"] +numbin = ["numbin"] [build-system] requires = ["maturin>=1.8,<2.0"] diff --git a/requirements/dev.txt b/requirements/dev.txt index e88f645f..b429b282 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,4 +1,3 @@ -setuptools_scm>=7 pytest>=8 pytest-mock>=3.5 mypy~=1.14 diff --git a/setup.py b/setup.py deleted file mode 100644 index e392c096..00000000 --- a/setup.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Build the package with Rust binary.""" - -import os -import shutil -import subprocess -from io import open - -from setuptools import Extension, find_packages, setup -from setuptools.command.build_ext import build_ext as _build_ext - -here = os.path.abspath(os.path.dirname(__file__)) -PACKAGE_NAME = "mosec" - -with open(os.path.join(here, "README.md"), encoding="utf-8") as f: - readme = f.read() - -with open(os.path.join(here, "requirements/dev.txt"), encoding="utf-8") as f: - dev_requirements = f.read().splitlines() - -with open(os.path.join(here, "requirements/doc.txt"), encoding="utf-8") as f: - doc_requirements = f.read().splitlines() - -with open(os.path.join(here, "requirements/mixin.txt"), encoding="utf-8") as f: - mixin_requirements = f.read().splitlines() - - -class RustExtension(Extension): - # pylint: disable=too-few-public-methods - """Custom Extension class for rust.""" - - -ext_modules = [] - -if os.getenv("PRODUCTION_MODE"): - ext_modules.append(RustExtension(name="mosec.bin", sources=["src/*"])) - - -class RustBuildExt(_build_ext): - """Custom build extension class for rust.""" - - def run(self): - """Run the build command.""" - for ext in self.extensions: - self.build_extension(ext) - - def build_extension(self, ext: Extension): - """Build the extension.""" - if not isinstance(ext, RustExtension): - return super().build_extension(ext) - - libpath = ext.name.replace(".", os.sep) - build_libpath = os.path.join(self.build_lib, libpath) - - rust_target = os.getenv("RUST_TARGET") - build_cmd = ["cargo", "build", "--release"] - if rust_target is not None: - build_cmd += ["--target", rust_target] - - print(f"running rust cargo package build: {build_cmd}") - errno = subprocess.call(build_cmd) - assert errno == 0, "Error occurred while building rust binary" - - # package the binary - if rust_target is not None: - target_dir = os.path.join("target", rust_target, "release", PACKAGE_NAME) - else: - target_dir = os.path.join("target", "release", PACKAGE_NAME) - os.makedirs(build_libpath, exist_ok=True) - shutil.copy(target_dir, build_libpath) - - if self.inplace: - os.makedirs(os.path.dirname(libpath), exist_ok=True) - shutil.copy(build_libpath, libpath) - return None - - -setup( - name=PACKAGE_NAME, - author="Keming Yang", - author_email="kemingy94@gmail.com", - description="Model Serving made Efficient in the Cloud.", - long_description=readme, - long_description_content_type="text/markdown", - url="https://github.com/mosecorg/mosec", - license="Apache License 2.0", - use_scm_version=True, - packages=find_packages(exclude=["examples*", "tests*"]), - include_package_data=True, - python_requires=">=3.8", - setup_requires=["setuptools_scm>=7.0"], - extras_require={ - "dev": dev_requirements, - "mixin": mixin_requirements, - "doc": doc_requirements, - }, - zip_safe=False, - ext_modules=ext_modules, # type: ignore - cmdclass={"build_ext": RustBuildExt}, # type: ignore -) diff --git a/tests/test_service.py b/tests/test_service.py index 87facf4f..59dfe846 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -89,8 +89,8 @@ def test_http2_service(mosec_service, http2_client): def test_square_service(mosec_service, http_client): resp = http_client.get("/") assert resp.status_code == HTTPStatus.OK - # only check the prefix since the version from setuptools_scm may not be the - # correct one used in `Cargo.toml` + # only check the prefix since the version comes from Cargo.toml and cannot be + # accessed here assert resp.headers["server"].startswith("mosec/"), f"{resp.headers['server']}" resp = http_client.get("/metrics")