From 74bc4c31718202c64031a59b80495ae1c6825ecd Mon Sep 17 00:00:00 2001 From: ZHU Yuhao Date: Wed, 1 Jul 2026 08:10:34 +0200 Subject: [PATCH 1/2] Update the codebase to mojo v1.0.0b2 --- .github/actions/setup-decimo/action.yml | 8 +- .github/workflows/run_tests.yaml | 34 +++---- .gitignore | 3 +- examples/run_bigfloat.sh | 10 +- pixi.lock | 116 ++++++++++++------------ pixi.toml | 29 +++--- src/decimo/bigdecimal/special.mojo | 5 + src/decimo/bigint/special.mojo | 2 + src/decimo/decimal128/decimal128.mojo | 22 +++-- tests/bigint/test_bigint_special.mojo | 23 ++--- tests/test.sh | 18 ++-- tests/test_cli.sh | 16 ++-- 12 files changed, 149 insertions(+), 137 deletions(-) diff --git a/.github/actions/setup-decimo/action.yml b/.github/actions/setup-decimo/action.yml index cab5207e..1709a65a 100644 --- a/.github/actions/setup-decimo/action.yml +++ b/.github/actions/setup-decimo/action.yml @@ -1,5 +1,5 @@ name: Setup Decimo with Pixi -description: Install pixi (cached), activate the Mojo environment, and download the prebuilt decimo.mojopkg artifact into tests/. +description: Install pixi (cached), activate the Mojo environment, and download the prebuilt decimo.mojoc artifact into tests/. runs: using: composite @@ -7,11 +7,11 @@ runs: - uses: prefix-dev/setup-pixi@v0.9.5 with: activate-environment: true - - name: Download decimo.mojopkg artifact + - name: Download decimo.mojoc artifact uses: actions/download-artifact@v4 with: - name: decimo-mojopkg + name: decimo-mojoc path: . - name: Stage package next to tests shell: bash - run: cp decimo.mojopkg tests/ + run: cp decimo.mojoc tests/ diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 93db1fe9..ee3a5e57 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -15,9 +15,9 @@ defaults: shell: bash # The `build` job is the gate: format check, doc generation, and the -# `decimo.mojopkg` build all run once. The package is uploaded as an +# `decimo.mojoc` build all run once. The package is uploaded as an # artifact so downstream test jobs can download it instead of rebuilding -# (saves ~30s of `mojo package` per job × 10 jobs). The pixi environment +# (saves ~30s of `mojo precompile` per job × 10 jobs). The pixi environment # is cached by `prefix-dev/setup-pixi@v0.9.5`, so test jobs no longer # `curl | sh` and `pixi install` from scratch. @@ -41,7 +41,7 @@ jobs: run: | for attempt in 1 2 3; do echo "=== build attempt $attempt ===" - if pixi run mojo package src/decimo; then + if pixi run mojo precompile src/decimo; then echo "=== build succeeded on attempt $attempt ===" break fi @@ -52,13 +52,13 @@ jobs: echo "=== build crashed, retrying in 5s... ===" sleep 5 done - - name: Stage decimo.mojopkg next to tests + - name: Stage decimo.mojoc next to tests # `pixi run doc` documents `src/cli/calculator`, which imports - # `decimo.*`. Mojo 1.0.0b1 cannot resolve those qualified + # `decimo.*`. Mojo cannot resolve those qualified # references when re-traversing the source tree via `-I src`, # so the doc task uses `-I tests` against the prebuilt - # `decimo.mojopkg`. Stage it here before invoking doc. - run: cp decimo.mojopkg tests/ + # `decimo.mojoc`. Stage it here before invoking doc. + run: cp decimo.mojoc tests/ - name: Generate docs (with retry for Mojo compiler intermittent crashes) run: | for attempt in 1 2 3; do @@ -74,13 +74,13 @@ jobs: echo "=== doc crashed, retrying in 5s... ===" sleep 5 done - - name: Verify decimo.mojopkg was produced - run: test -f decimo.mojopkg - - name: Upload decimo.mojopkg artifact + - name: Verify decimo.mojoc was produced + run: test -f decimo.mojoc + - name: Upload decimo.mojoc artifact uses: actions/upload-artifact@v4 with: - name: decimo-mojopkg - path: decimo.mojopkg + name: decimo-mojoc + path: decimo.mojoc retention-days: 1 if-no-files-found: error @@ -287,7 +287,7 @@ jobs: done # ── Test: CLI ──────────────────────────────────────────────────────────────── - # The CLI test needs a freshly built binary, not the .mojopkg, so we + # The CLI test needs a freshly built binary, not the .mojoc, so we # invoke `pixi run buildcli` here. Uses cached pixi env (no curl install, # no second `pixi install` from scratch). test-cli: @@ -316,13 +316,13 @@ jobs: echo "=== build crashed, retrying in 5s... ===" sleep 5 done - - name: Stage decimo.mojopkg next to tests + - name: Stage decimo.mojoc next to tests # `tests/test.sh run_cli` and `run_bigfloat` use `-I tests` to load - # the prebuilt `decimo.mojopkg` (Mojo 1.0.0b1's `mojo run/build` + # the prebuilt `decimo.mojoc` (Mojo's `mojo run/build` # cannot resolve `decimo.X.Y.foo` qualified references when # re-traversing source via `-I src`). `pixi run buildcli` produces - # the package at `temp/decimo.mojopkg`, so stage a copy here. - run: cp temp/decimo.mojopkg tests/ + # the package at `temp/decimo.mojoc`, so stage a copy here. + run: cp temp/decimo.mojoc tests/ - name: Run tests (with retry for Mojo compiler intermittent crashes) run: | for attempt in 1 2 3; do diff --git a/.gitignore b/.gitignore index d7e46ee4..477b7712 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,9 @@ .magic magic.lock -# mojopkg files +# mojo pre-compiled files *.mojopkg +*.mojoc # Editor environments .vscode diff --git a/examples/run_bigfloat.sh b/examples/run_bigfloat.sh index 99d69c0c..b56a481f 100755 --- a/examples/run_bigfloat.sh +++ b/examples/run_bigfloat.sh @@ -8,7 +8,7 @@ # # What it does: # 1. Ensures the GMP/MPFR C wrapper (libdecimo_gmp_wrapper) is built. -# 2. Ensures tests/decimo.mojopkg is available (built on demand). +# 2. Ensures tests/decimo.mojoc is available (built on demand). # 3. Builds with `-I tests`, debug info, and ASSERT=all, # linking against the C wrapper. Output binary goes to temp/. # 4. Executes the binary with DYLD_LIBRARY_PATH / LD_LIBRARY_PATH set @@ -43,10 +43,10 @@ if [[ ! -f "$WRAPPER_LIB" ]]; then bash "$WRAPPER_DIR/build_gmp_wrapper.sh" fi -# 2. Ensure decimo.mojopkg exists for `-I tests`. -if [[ ! -f tests/decimo.mojopkg ]]; then - echo "==> Building tests/decimo.mojopkg..." - pixi run mojo package src/decimo -o tests/decimo.mojopkg +# 2. Ensure decimo.mojoc exists for `-I tests`. +if [[ ! -f tests/decimo.mojoc ]]; then + echo "==> Building tests/decimo.mojoc..." + pixi run mojo precompile src/decimo -o tests/decimo.mojoc fi # 3. Build the user's .mojo file into temp/. diff --git a/pixi.lock b/pixi.lock index d40b3287..c194b808 100644 --- a/pixi.lock +++ b/pixi.lock @@ -11,7 +11,7 @@ environments: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - - conda: https://repo.prefix.dev/modular-community/linux-64/argmojo-0.6.0-hb0f4dca_0.conda + - conda: https://repo.prefix.dev/modular-community/linux-64/argmojo-0.7.0-hb0f4dca_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda @@ -60,11 +60,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.modular.com/max/noarch/mblack-26.3.0-release.conda + - conda: https://conda.modular.com/max/noarch/mblack-26.4.0-release.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - - conda: https://conda.modular.com/max/linux-64/mojo-1.0.0b1-release.conda - - conda: https://conda.modular.com/max/linux-64/mojo-compiler-1.0.0b1-release.conda - - conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b1-release.conda + - conda: https://conda.modular.com/max/linux-64/mojo-1.0.0b2-release.conda + - conda: https://conda.modular.com/max/linux-64/mojo-compiler-1.0.0b2-release.conda + - conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b2-release.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda @@ -106,7 +106,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - - conda: https://repo.prefix.dev/modular-community/osx-arm64/argmojo-0.6.0-h60d57d3_0.conda + - conda: https://repo.prefix.dev/modular-community/osx-arm64/argmojo-0.7.0-h60d57d3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda @@ -145,11 +145,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.1-h1b79a29_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - - conda: https://conda.modular.com/max/noarch/mblack-26.3.0-release.conda + - conda: https://conda.modular.com/max/noarch/mblack-26.4.0-release.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - - conda: https://conda.modular.com/max/osx-arm64/mojo-1.0.0b1-release.conda - - conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-1.0.0b1-release.conda - - conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b1-release.conda + - conda: https://conda.modular.com/max/osx-arm64/mojo-1.0.0b2-release.conda + - conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-1.0.0b2-release.conda + - conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b2-release.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda @@ -211,22 +211,22 @@ packages: license_family: MIT size: 8191 timestamp: 1744137672556 -- conda: https://repo.prefix.dev/modular-community/linux-64/argmojo-0.6.0-hb0f4dca_0.conda - sha256: 55c8077091632b4a012e53a7dbbec4dceac4171503d92cc6372f62e8611900ed - md5: 49da259bda4189b4807c8827357e23fe +- conda: https://repo.prefix.dev/modular-community/linux-64/argmojo-0.7.0-hb0f4dca_0.conda + sha256: d1549ec978832a8c6a18454265ac4d89ad69e27967498fd175e6152174c4e6ab + md5: bc165382aa8c5dbf6a2c5b6c3d6c40e2 depends: - - mojo-compiler 1.0.0b1.* + - mojo-compiler 1.0.0b2.* license: Apache-2.0 - size: 2113584 - timestamp: 1778610927336 -- conda: https://repo.prefix.dev/modular-community/osx-arm64/argmojo-0.6.0-h60d57d3_0.conda - sha256: 4504bc8420f20611cdcebab8280b24681af3921bcc29ad2e197301cc44b28ff2 - md5: 88c4863e9de3507dba925c4f48a3bd53 + size: 561359 + timestamp: 1782868006869 +- conda: https://repo.prefix.dev/modular-community/osx-arm64/argmojo-0.7.0-h60d57d3_0.conda + sha256: 602b2b28f33a480aee4fa789cacb2c9abf4e71ee9a73df072bcf3d9da04f2886 + md5: 0bf4d67c5f332d2ea61d613afd968a15 depends: - - mojo-compiler 1.0.0b1.* + - mojo-compiler 1.0.0b2.* license: Apache-2.0 - size: 2112885 - timestamp: 1778604915940 + size: 560821 + timestamp: 1782867689075 - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda sha256: e1c3dc8b5aa6e12145423fed262b4754d70fec601339896b9ccf483178f690a6 md5: 767d508c1a67e02ae8f50e44cacfadb2 @@ -929,10 +929,10 @@ packages: license_family: MIT size: 69017 timestamp: 1778169663339 -- conda: https://conda.modular.com/max/noarch/mblack-26.3.0-release.conda +- conda: https://conda.modular.com/max/noarch/mblack-26.4.0-release.conda noarch: python - sha256: c7c36d5b223862acffaaacdfc6f672ca198a046a66f4a956ca57933123fb93b2 - md5: 63c657fb1780079c3f6cbb7a758a16d2 + sha256: e2497cbefbf5d962cd0db8c1b893e7e48d8f3603cab84a9d8bad0030b5bb26b1 + md5: ec6bf2b9588f5ba4bd3472c0773c4892 depends: - python >=3.10 - click >=8.0.0 @@ -942,8 +942,8 @@ packages: - platformdirs >=2 - tomli >=1.1.0 license: LicenseRef-Modular-Proprietary - size: 135000 - timestamp: 1777574970629 + size: 135301 + timestamp: 1781220755102 - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 md5: 592132998493b3ff25fd7479396e8351 @@ -953,53 +953,53 @@ packages: license_family: MIT size: 14465 timestamp: 1733255681319 -- conda: https://conda.modular.com/max/linux-64/mojo-1.0.0b1-release.conda - sha256: a8f36a83ece187307d7d78c152828eac58edb002d515de2bcbe9f5a19388b70c - md5: 11bdf0256d179b89dc00ca4150c6963c +- conda: https://conda.modular.com/max/linux-64/mojo-1.0.0b2-release.conda + sha256: 7553366c138bae2fcf8617582f1bb6d6b6742087bd6a83ea83aa41c023499382 + md5: 8d4390edde2dcf29880c57b3088f15aa depends: - python >=3.10 - - mojo-compiler ==1.0.0b1 - - mblack ==26.3.0 + - mojo-compiler ==1.0.0b2 + - mblack ==26.4.0 - jupyter_client >=8.6.2,<8.7 license: LicenseRef-Modular-Proprietary - size: 94106548 - timestamp: 1777595784229 -- conda: https://conda.modular.com/max/osx-arm64/mojo-1.0.0b1-release.conda - sha256: 2980b825e3eedb153f68f74edeaae6a7b819912c3eb6e49f1eed7d25f883e050 - md5: b5f2c6b04a9e2bbe199fef1ea5677014 + size: 96475709 + timestamp: 1781237127405 +- conda: https://conda.modular.com/max/osx-arm64/mojo-1.0.0b2-release.conda + sha256: 39bb59a309af5e4c78c077263e5824e33bba8159a1e0fc39ff2786a1f9d5762b + md5: 1a6b6dd12763032e46b956407b39ab48 depends: - python >=3.10 - - mojo-compiler ==1.0.0b1 - - mblack ==26.3.0 + - mojo-compiler ==1.0.0b2 + - mblack ==26.4.0 - jupyter_client >=8.6.2,<8.7 license: LicenseRef-Modular-Proprietary - size: 83375027 - timestamp: 1777596028766 -- conda: https://conda.modular.com/max/linux-64/mojo-compiler-1.0.0b1-release.conda - sha256: 706c7f8e2be740dda8039bf96182b578bfbbd9220ca1a7f762e1f82acc4eb784 - md5: 2fc6038c3837e50bb855bcf7f930c1c9 + size: 84573003 + timestamp: 1781237324882 +- conda: https://conda.modular.com/max/linux-64/mojo-compiler-1.0.0b2-release.conda + sha256: 87f8e9ef573f37072423aeb56a7c468bdb5f5889476bce4aa7c3396665c651fe + md5: 6be85d17872f5c2370015ca4ec07dd0d depends: - - mojo-python ==1.0.0b1 + - mojo-python ==1.0.0b2 license: LicenseRef-Modular-Proprietary - size: 89421276 - timestamp: 1777595833113 -- conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-1.0.0b1-release.conda - sha256: 3e0d6e6e814cb39d14d24234eb8f2ac6524379ea0a9c398a54e77598844e96d6 - md5: fe08c02512b02563a0b878eeb32a684b + size: 85342385 + timestamp: 1781237127405 +- conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-1.0.0b2-release.conda + sha256: 91c4d590a152ec2e26846955fcd7ec02796dfaffefa006a1c0c5790575be2051 + md5: cd8d3f4a22e5f14dd909d0d5570c4940 depends: - - mojo-python ==1.0.0b1 + - mojo-python ==1.0.0b2 license: LicenseRef-Modular-Proprietary - size: 67503416 - timestamp: 1777596016502 -- conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b1-release.conda + size: 62655166 + timestamp: 1781237320083 +- conda: https://conda.modular.com/max/noarch/mojo-python-1.0.0b2-release.conda noarch: python - sha256: b0554c7705685c86e8a8063bcd11ed58897a498c8e1e19ef045f5ebba2c8874f - md5: d0ad2f8ba3758f03bfda84d92bb34b67 + sha256: 37bf44a74edcd2ebaec7fe8299ef183e006ad051bfcf21caa06becc0e0eeffd5 + md5: 8781bd673909835fa59ce818e3835839 depends: - python >=3.10 license: LicenseRef-Modular-Proprietary - size: 23114 - timestamp: 1777594699418 + size: 24682 + timestamp: 1781220753246 - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.0.2-pyhcf101f3_0.conda sha256: 74f7b461e0f0e0709a0c8abb018de9ad885258b74790ffda1e750ac5ddde0a85 md5: b874955758a30a37c78b82ea5cf78fdb diff --git a/pixi.toml b/pixi.toml index 8de97ec2..0fc321f9 100644 --- a/pixi.toml +++ b/pixi.toml @@ -9,8 +9,8 @@ readme = "README.md" version = "0.10.0" [dependencies] -argmojo = ">=0.6.0,<0.7" # CLI argument parsing for the Decimo calculator -mojo = ">=1.0.0b1,<2" # Mojo language compiler and runtime +argmojo = ">=0.7,<0.8" # CLI argument parsing for the Decimo calculator +mojo = ">=1.0.0b2,<2" # Mojo language compiler and runtime python = ">=3.13" # For Python bindings and tests python-build = ">=0.2.0" # Build PyPI wheel (`pixi run wheel`) ruff = ">=0.15.0" # Python code formatter @@ -27,7 +27,7 @@ format = """pixi run mojo format ./src \ # doc # Note: the CLI calculator doc step uses `-I tests` (the prebuilt -# `decimo.mojopkg`) instead of `-I src`. Mojo 1.0.0b1's `mojo doc` cannot +# `decimo.mojoc`) instead of `-I src`. Mojo's `mojo doc` cannot # resolve `decimo.X.Y.foo`-style qualified references when re-traversing # the source tree via `-I src`. Loading the pre-built package via # `-I tests` sidesteps that regression. The `package` task below is @@ -40,15 +40,15 @@ p = "clear && pixi run package" package = """pixi run format \ &&pixi run package_decimo \ &&pixi run doc""" -package_decimo = """pixi run mojo package src/decimo \ -&&cp decimo.mojopkg tests/ \ -&&cp decimo.mojopkg benches/ \ -&&rm decimo.mojopkg""" +package_decimo = """pixi run mojo precompile src/decimo -o decimo.mojoc \ +&&cp decimo.mojoc tests/ \ +&&cp decimo.mojoc benches/ \ +&&rm decimo.mojoc""" # clean the package files in tests folder c = "clear && pixi run clean" -clean = """rm tests/decimo.mojopkg && \ -rm benches/decimo.mojopkg""" +clean = """rm tests/decimo.mojoc && \ +rm benches/decimo.mojoc""" # gmp/mpfr wrapper (compile C wrapper — no MPFR needed at build time) bgmp = "clear && pixi run buildgmp" @@ -96,21 +96,21 @@ bcli = "clear && pixi run buildcli" # step is needed. We still package decimo into temp/ so the CLI build # (and downstream `testcli`) can use `-I temp`. buildcli = """mkdir -p temp \ -&& pixi run mojo package src/decimo -o temp/decimo.mojopkg \ +&& pixi run mojo precompile src/decimo -o temp/decimo.mojoc \ && pixi run mojo build -I temp -I src/cli -o decimo src/cli/main.mojo""" # Uncomment the following lines if we build the CLI package with a # local clone of argmojo (e.g. when conda argmojo is unavailable): # buildcli = """pixi run fetch \ # && mkdir -p temp \ -# && pixi run mojo package src/decimo -o temp/decimo.mojopkg \ -# && pixi run mojo package temp/argmojo/src/argmojo -o temp/argmojo.mojopkg \ +# && pixi run mojo precompile src/decimo -o temp/decimo.mojoc \ +# && pixi run mojo precompile temp/argmojo/src/argmojo -o temp/argmojo.mojoc \ # && pixi run mojo build -I temp -I src/cli -o decimo src/cli/main.mojo""" tcli = "clear && pixi run test cli" -testcli = "pixi run buildcli && cp temp/decimo.mojopkg tests/ && bash tests/test.sh cli" +testcli = "pixi run buildcli && cp temp/decimo.mojoc tests/ && bash tests/test.sh cli" # python bindings (mojo4py) bpy = "clear && pixi run buildpy" -buildpy = """pixi run mojo package src/decimo -o tests/decimo.mojopkg \ +buildpy = """pixi run mojo precompile src/decimo -o tests/decimo.mojoc \ && pixi run mojo build python/decimo_module.mojo \ --emit shared-lib -I tests -o python/src/decimo/_decimo.so""" testpy = "pixi run buildpy && pixi run python python/tests/test_decimo.py" @@ -121,7 +121,6 @@ wheel = "cd python && pixi run python -m build --wheel" # All task all = """pixi run format \ &&pixi run package \ -&&pixi run doc \ &&pixi run test \ &&pixi run buildcli \ &&pixi run testcli \ diff --git a/src/decimo/bigdecimal/special.mojo b/src/decimo/bigdecimal/special.mojo index b122d985..262cdca7 100644 --- a/src/decimo/bigdecimal/special.mojo +++ b/src/decimo/bigdecimal/special.mojo @@ -27,6 +27,7 @@ from decimo.errors import ValueError # requested precision and the digit count of `n`. Covers the rounding error # that accumulates over the `n` intermediate products. comptime FACTORIAL_GUARD_DIGITS = 9 # word size +"""Extra guard digits carried during a rounded `factorial`.""" # Largest argument accepted by `factorial`. Even 10^6 already needs ~10^6 # multiplications, so anything beyond it is impractical with the simple @@ -34,6 +35,7 @@ comptime FACTORIAL_GUARD_DIGITS = 9 # word size # so an out-of-range argument raises a clear error instead of an `Int` # overflow. (A faster algorithm, e.g. binary splitting, could lift this.) comptime FACTORIAL_MAX_INPUT = 1_000_000 +"""The largest argument accepted by `factorial` (10^6).""" def factorial(x: BigDecimal, precision: Int = 0) raises -> BigDecimal: @@ -113,6 +115,9 @@ def product_range(low: Int, high: Int) raises -> BigDecimal: Returns: `low * (low + 1) * ... * high` (1 when the range is empty). + + Raises: + Error: Propagated from an intermediate `BigDecimal` multiplication. """ if low > high: return BigDecimal(1) diff --git a/src/decimo/bigint/special.mojo b/src/decimo/bigint/special.mojo index ac0e3834..5adc4511 100644 --- a/src/decimo/bigint/special.mojo +++ b/src/decimo/bigint/special.mojo @@ -30,6 +30,7 @@ from decimo.errors import ValueError # so an out-of-range argument raises a clear error instead of an `Int` # overflow. (A faster algorithm, e.g. binary splitting, could lift this.) comptime FACTORIAL_MAX_INPUT = 1_000_000 +"""The largest argument accepted by `factorial` (10^6).""" # Below this many factors, `product_range` stops splitting and accumulates the # consecutive factors with in-place single-word multiplies instead. That avoids @@ -37,6 +38,7 @@ comptime FACTORIAL_MAX_INPUT = 1_000_000 # products near the bottom of the recursion. Measured ~1.1x (large n) to ~4x # (small n) faster than splitting all the way down. comptime PRODUCT_RANGE_LEAF_CUTOFF = 32 +"""Maximum factors in a `product_range` leaf before it binary-splits.""" def factorial(x: BigInt) raises -> BigInt: diff --git a/src/decimo/decimal128/decimal128.mojo b/src/decimo/decimal128/decimal128.mojo index 565ccd9a..dfb4dfa2 100644 --- a/src/decimo/decimal128/decimal128.mojo +++ b/src/decimo/decimal128/decimal128.mojo @@ -23,7 +23,7 @@ operation dunders, and other dunders that implement traits, as well as mathematical methods that do not implement a trait. """ -from std.memory import UnsafePointer +from std.memory import Span, UnsafePointer from std.hashlib.hasher import Hasher import decimo.decimal128.arithmetics as decimal128_arithmetics @@ -1136,8 +1136,9 @@ struct Decimal128( if scale == 0: writer.write( StringSlice( - ptr=buf.unsafe_ptr() + pos, - length=n_digits, + unsafe_from_utf8=Span( + ptr=buf.unsafe_ptr() + pos, length=n_digits + ) ) ) elif scale >= n_digits: @@ -1154,23 +1155,26 @@ struct Decimal128( writer.write("0.") writer.write( StringSlice( - ptr=buf.unsafe_ptr() + frac_start, - length=scale, + unsafe_from_utf8=Span( + ptr=buf.unsafe_ptr() + frac_start, length=scale + ) ) ) else: var int_len = n_digits - scale writer.write( StringSlice( - ptr=buf.unsafe_ptr() + pos, - length=int_len, + unsafe_from_utf8=Span( + ptr=buf.unsafe_ptr() + pos, length=int_len + ) ) ) writer.write(".") writer.write( StringSlice( - ptr=buf.unsafe_ptr() + pos + int_len, - length=scale, + unsafe_from_utf8=Span( + ptr=buf.unsafe_ptr() + pos + int_len, length=scale + ) ) ) diff --git a/tests/bigint/test_bigint_special.mojo b/tests/bigint/test_bigint_special.mojo index 57de5a9f..606d4319 100644 --- a/tests/bigint/test_bigint_special.mojo +++ b/tests/bigint/test_bigint_special.mojo @@ -9,7 +9,7 @@ from decimo.bigint.special import product_range def test_factorial_basic() raises: - """Test factorial of small non-negative integers.""" + """Tests factorial of small non-negative integers.""" testing.assert_equal(String(BigInt(0).factorial()), "1") testing.assert_equal(String(BigInt(1).factorial()), "1") testing.assert_equal(String(BigInt(2).factorial()), "2") @@ -19,14 +19,14 @@ def test_factorial_basic() raises: def test_factorial_large() raises: - """Test factorial that exceeds 64-bit range.""" + """Tests factorial that exceeds 64-bit range.""" testing.assert_equal( String(BigInt(25).factorial()), "15511210043330985984000000" ) def test_factorial_crosses_leaf_cutoff() raises: - """Test factorial large enough to exercise the binary-split branch.""" + """Tests factorial large enough to exercise the binary-split branch.""" # 40 > the 32-factor leaf cutoff, so this splits and recombines. testing.assert_equal( String(BigInt(40).factorial()), @@ -35,7 +35,7 @@ def test_factorial_crosses_leaf_cutoff() raises: def test_factorial_negative_raises() raises: - """Test that a negative argument raises.""" + """Tests that a negative argument raises.""" var raised = False try: _ = BigInt(-1).factorial() @@ -45,7 +45,7 @@ def test_factorial_negative_raises() raises: def test_factorial_too_large_raises() raises: - """Test that an argument above the cap raises.""" + """Tests that an argument above the cap raises.""" var raised = False try: _ = BigInt(2_000_000).factorial() # above the 10^6 cap @@ -55,7 +55,7 @@ def test_factorial_too_large_raises() raises: def test_permutation() raises: - """Test permutation P(n, k).""" + """Tests permutation P(n, k).""" testing.assert_equal(String(BigInt(10).permutation(3)), "720") testing.assert_equal(String(BigInt(5).permutation(5)), "120") # P(n,n)=n! testing.assert_equal(String(BigInt(5).permutation(0)), "1") @@ -64,7 +64,7 @@ def test_permutation() raises: def test_permutation_negative_k_raises() raises: - """Test that a negative k raises.""" + """Tests that a negative k raises.""" var raised = False try: _ = BigInt(5).permutation(-1) @@ -74,13 +74,13 @@ def test_permutation_negative_k_raises() raises: def test_permutation_large_n() raises: - """Test permutation with a large n that still fits in one word.""" + """Tests permutation with a large n that still fits in one word.""" # n = 70000 (< 2^32), k = 2 -> 70000 * 69999. testing.assert_equal(String(BigInt(70000).permutation(2)), "4899930000") def test_permutation_n_too_large_raises() raises: - """Test that an n above 2^32 - 1 raises.""" + """Tests that an n above 2^32 - 1 raises.""" var raised = False try: _ = BigInt(4_294_967_296).permutation(2) # 2^32, above WORD_MAX @@ -90,7 +90,7 @@ def test_permutation_n_too_large_raises() raises: def test_multiply_by_word_inplace_zero_and_one() raises: - """word == 1 is a no-op; word == 0 yields a canonical zero.""" + """Tests that word == 1 is a no-op; word == 0 yields a canonical zero.""" var x = BigInt(12345) multiply_by_word_inplace(x, 1) testing.assert_equal(String(x), "12345") @@ -109,7 +109,8 @@ def test_multiply_by_word_inplace_preserves_sign() raises: def test_product_range_out_of_bounds_raises() raises: - """product_range rejects bounds that don't fit in a single word.""" + """Tests that product_range rejects bounds that don't fit in a single word. + """ var raised = False try: _ = product_range(1, 4_294_967_296) # high = 2^32 > WORD_MAX diff --git a/tests/test.sh b/tests/test.sh index fa056f27..0c6a939a 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -29,19 +29,19 @@ set -eo pipefail REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd) cd "$REPO_ROOT" -# ── Preflight: ensure tests/decimo.mojopkg exists ─────────────────────────── +# ── Preflight: ensure tests/decimo.mojoc exists ───────────────────────────── # All Mojo test invocations below use `-I tests` to pick up the prebuilt -# `decimo.mojopkg` (Mojo 1.0.0b1's `mojo run` cannot resolve -# `decimo.X.Y.foo` qualified references when re-traversing source via -# `-I src`). On a fresh checkout the package may not exist yet, so build -# it on demand. CI normally stages a prebuilt artifact via the -# `setup-decimo` action, in which case this is a no-op. +# `decimo.mojoc` (`mojo run` cannot resolve `decimo.X.Y.foo` qualified +# references when re-traversing source via `-I src`). On a fresh checkout the +# package may not exist yet, so build it on demand. CI normally stages a +# prebuilt artifact via the `setup-decimo` action, in which case this is a +# no-op. ensure_decimo_mojopkg() { - if [[ -f tests/decimo.mojopkg ]]; then + if [[ -f tests/decimo.mojoc ]]; then return 0 fi - echo "tests/decimo.mojopkg not found; building it now..." - pixi run mojo package src/decimo -o tests/decimo.mojopkg + echo "tests/decimo.mojoc not found; building it now..." + pixi run mojo precompile src/decimo -o tests/decimo.mojoc } # ── Suite definitions ──────────────────────────────────────────────────────── diff --git a/tests/test_cli.sh b/tests/test_cli.sh index 760ff180..1dbd7b41 100644 --- a/tests/test_cli.sh +++ b/tests/test_cli.sh @@ -6,14 +6,14 @@ set -e # Exit immediately if any command fails REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd) cd "$REPO_ROOT" -# ── Preflight: ensure tests/decimo.mojopkg exists ─────────────────────────── -# CLI unit tests use `-I tests` to pick up the prebuilt `decimo.mojopkg` -# (Mojo 1.0.0b1's `mojo run` cannot resolve `decimo.X.Y.foo` qualified -# references when re-traversing source via `-I src`). On a fresh checkout -# the package may not exist yet, so build it on demand. -if [[ ! -f tests/decimo.mojopkg ]]; then - echo "tests/decimo.mojopkg not found; building it now..." - pixi run mojo package src/decimo -o tests/decimo.mojopkg +# ── Preflight: ensure tests/decimo.mojoc exists ───────────────────────────── +# CLI unit tests use `-I tests` to pick up the prebuilt `decimo.mojoc` +# (`mojo run` cannot resolve `decimo.X.Y.foo` qualified references when +# re-traversing source via `-I src`). On a fresh checkout the package may +# not exist yet, so build it on demand. +if [[ ! -f tests/decimo.mojoc ]]; then + echo "tests/decimo.mojoc not found; building it now..." + pixi run mojo precompile src/decimo -o tests/decimo.mojoc fi # ── Unit tests ───────────────────────────────────────────────────────────── From 44cb953f85b8f5c9aaf9cd95ba06e7dfc78e8633 Mon Sep 17 00:00:00 2001 From: ZHU Yuhao Date: Wed, 1 Jul 2026 19:44:05 +0200 Subject: [PATCH 2/2] Address comments --- .github/workflows/run_tests.yaml | 2 +- docs/changelog.md | 6 ++++++ docs/plans/gmp_integration.md | 6 +++--- docs/plans/mojo4py.md | 6 +++--- docs/readme_unreleased.md | 6 +++--- src/decimo/decimal128/utility.mojo | 2 +- tests/bigint/test_bigint_special.mojo | 3 +-- tests/test.sh | 8 ++++---- 8 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index ee3a5e57..cb1055fc 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -41,7 +41,7 @@ jobs: run: | for attempt in 1 2 3; do echo "=== build attempt $attempt ===" - if pixi run mojo precompile src/decimo; then + if pixi run mojo precompile src/decimo -o decimo.mojoc; then echo "=== build succeeded on attempt $attempt ===" break fi diff --git a/docs/changelog.md b/docs/changelog.md index 6b2a2ccc..04faf1c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,12 @@ This is a list of changes for the Decimo package (formerly DeciMojo). ## Unreleased +1. **Migrated the codebase to Mojo v1.0.0b2.** Bumped the Pixi `mojo` + dependency to `>=1.0.0b2` and `argmojo` to `0.7.0`. Switched packaging + from the deprecated `mojo package` / `.mojopkg` to `mojo precompile` / + `.mojoc` across Pixi tasks, CI workflows, and helper scripts, and + updated the `Decimal128` string formatter to the + `StringSlice(unsafe_from_utf8=Span(...))` constructor (PR #257). 1. **Renamed `BigDecimal` rounding APIs to `*_inplace`.** The free function `decimo.bigdecimal.rounding.round_to_precision` and the `BigDecimal.round_to_precision` method were renamed to diff --git a/docs/plans/gmp_integration.md b/docs/plans/gmp_integration.md index 591f4034..fe1af9ff 100644 --- a/docs/plans/gmp_integration.md +++ b/docs/plans/gmp_integration.md @@ -813,8 +813,8 @@ Link against whichever is present. The stub library lets Decimo compile and run **Phase 1 (immediate)**: Use **Option A** — compile-time feature flag. Build two package variants: -- `decimo.mojopkg` — native only (default) -- `decimo_gmp.mojopkg` — with GMP backend +- `decimo.mojoc` — native only (default) +- `decimo_gmp.mojoc` — with GMP backend **Phase 2 (when Mojo matures)**: Migrate to **Option B** with `DLHandle` when available, enabling true runtime detection. @@ -1132,7 +1132,7 @@ build-gmp-wrapper = """ package-with-gmp = { depends-on = ["build-gmp-wrapper"] } package-with-gmp.cmd = """ - mojo package src/decimo -o decimo.mojopkg && \ + mojo precompile src/decimo -o decimo.mojoc && \ cp src/decimo/gmp/libgmp_wrapper.dylib . """ ``` diff --git a/docs/plans/mojo4py.md b/docs/plans/mojo4py.md index 52930918..8279da25 100644 --- a/docs/plans/mojo4py.md +++ b/docs/plans/mojo4py.md @@ -12,7 +12,7 @@ Modular has introduced a beta mechanism that allows Mojo code to be exposed as a standard CPython extension module (`.so` / `.dylib`). This means a Python user can write `import decimo` and get access to Mojo-native `Decimal128`, `BigDecimal`, `BigInt`, and `BigUint` types at near-native speed, without rewriting anything in Python. -**Feasibility verdict: Possible but non-trivial.** The main (no surprise) blocker is that decimo is a *packaged* Mojo library (`.mojopkg`), not a single `.mojo` file. The Mojo importer hook (the easy dev-time path) does not support custom import paths for non-stdlib Mojo packages. The `.so` build path (the distribution path) works fine. This means the developer workflow is slightly more manual, but distribution is fully viable. +**Feasibility verdict: Possible but non-trivial.** The main (no surprise) blocker is that decimo is a *packaged* Mojo library (`.mojoc`), not a single `.mojo` file. The Mojo importer hook (the easy dev-time path) does not support custom import paths for non-stdlib Mojo packages. The `.so` build path (the distribution path) works fine. This means the developer workflow is slightly more manual, but distribution is fully viable. --- @@ -25,7 +25,7 @@ Modular has introduced a beta mechanism that allows Mojo code to be exposed as a | **Source import hook** | `import mojo.importer` in Python, then `import mojo_module` (auto-compiles `.mojo` → `.so` into `__mojocache__/`) | Dev prototyping with single-file modules only | | **Pre-built `.so`** | `mojo build mojo_module.mojo --emit shared-lib -o mojo_module.so` | Production, packages with dependencies, CI/CD | -For decimo, **only the pre-built `.so` path is viable** because the binding code will `import decimo` (the `.mojopkg`), and the importer hook cannot resolve that path. +For decimo, **only the pre-built `.so` path is viable** because the binding code will `import decimo` (the `.mojoc`), and the importer hook cannot resolve that path. ### 2.2 The Binding Pattern @@ -183,7 +183,7 @@ tpy = "clear && pixi run testpy" wheel = "cd python && pixi run python -m build --wheel" ``` -- `pixi run buildpy` — compiles the Mojo binding directly into the installable package at `python/src/decimo/_decimo.so`. No need to pre-package `decimo.mojopkg`; the `-I src` flag resolves `import decimo` to `src/decimo/`. +- `pixi run buildpy` — compiles the Mojo binding directly into the installable package at `python/src/decimo/_decimo.so`. No need to pre-package `decimo.mojoc`; the `-I src` flag resolves `import decimo` to `src/decimo/`. - `pixi run testpy` — builds then runs the Python test suite. - `pixi run wheel` — produces a pure-Python placeholder wheel in `python/dist/` (no `.so` included); suitable for PyPI name reservation. diff --git a/docs/readme_unreleased.md b/docs/readme_unreleased.md index 73e491a3..58dec955 100644 --- a/docs/readme_unreleased.md +++ b/docs/readme_unreleased.md @@ -400,7 +400,7 @@ Bug reports and feature requests are welcome! If you encounter issues, please [f ```text decimo/ ├── src/ # All source code -│ ├── decimo/ # Core library (mojo package) +│ ├── decimo/ # Core library (mojo precompile) │ │ ├── bigdecimal/ # Arbitrary-precision decimal (Decimal) │ │ ├── bigint/ # Arbitrary-precision signed integer (Integer) │ │ ├── bigint10/ # Base-10 signed integer (BigInt10) @@ -409,7 +409,7 @@ decimo/ │ │ └── ... # Shared utilities (str, errors, rounding) │ └── cli/ # CLI calculator application │ ├── main.mojo # Entry point (ArgMojo CLI) -│ └── calculator/ # Calculator engine (mojo package) +│ └── calculator/ # Calculator engine (mojo precompile) │ ├── tokenizer.mojo # Lexer: expression → tokens │ ├── parser.mojo # Shunting-yard: infix → RPN │ └── evaluator.mojo # RPN evaluator using Decimal @@ -425,7 +425,7 @@ decimo/ └── pixi.toml # Project configuration and tasks ``` -`src/decimo/` is a Mojo package — it is compiled with `mojo package` and can be imported by external projects. The TOML parser (`decimo.toml`) is included as a subpackage. `src/cli/` is an application that consumes the `decimo` package and compiles to a standalone binary via `mojo build`. +`src/decimo/` is a Mojo package — it is compiled with `mojo precompile` and can be imported by external projects. The TOML parser (`decimo.toml`) is included as a subpackage. `src/cli/` is an application that consumes the `decimo` package and compiles to a standalone binary via `mojo build`. ## Tests and benches diff --git a/src/decimo/decimal128/utility.mojo b/src/decimo/decimal128/utility.mojo index 2846dbb9..181b4051 100644 --- a/src/decimo/decimal128/utility.mojo +++ b/src/decimo/decimal128/utility.mojo @@ -764,7 +764,7 @@ def number_of_bits[dtype: DType, //](var value: Scalar[dtype]) -> Int: # up to 58, which is the maximum number of digits we need to handle for # Dec128 coefficients. # -# TODO: Currently, this won't work when you create a mojopkg to use. +# TODO: Currently, this won't work when you create a .mojoc package to use. # When Mojo supports module-level variables, this part can be used. # ===----------------------------------------------------------------------=== # diff --git a/tests/bigint/test_bigint_special.mojo b/tests/bigint/test_bigint_special.mojo index 606d4319..2699d73e 100644 --- a/tests/bigint/test_bigint_special.mojo +++ b/tests/bigint/test_bigint_special.mojo @@ -109,8 +109,7 @@ def test_multiply_by_word_inplace_preserves_sign() raises: def test_product_range_out_of_bounds_raises() raises: - """Tests that product_range rejects bounds that don't fit in a single word. - """ + """Tests that product_range rejects out-of-range bounds.""" var raised = False try: _ = product_range(1, 4_294_967_296) # high = 2^32 > WORD_MAX diff --git a/tests/test.sh b/tests/test.sh index 0c6a939a..7522757a 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -36,7 +36,7 @@ cd "$REPO_ROOT" # package may not exist yet, so build it on demand. CI normally stages a # prebuilt artifact via the `setup-decimo` action, in which case this is a # no-op. -ensure_decimo_mojopkg() { +ensure_decimo_package() { if [[ -f tests/decimo.mojoc ]]; then return 0 fi @@ -48,7 +48,7 @@ ensure_decimo_mojopkg() { run_mojo_suite() { local dir="$1" - ensure_decimo_mojopkg + ensure_decimo_package for f in tests/"$dir"/*.mojo; do echo "=== $f ===" # Retry once on transient Python init crash (libpython sporadic load failure). @@ -80,7 +80,7 @@ run_toml() { run_mojo_suite toml; } run_bigfloat() { # BigFloat tests require the C wrapper (libdecimo_gmp_wrapper) and MPFR. - ensure_decimo_mojopkg + ensure_decimo_package local WRAPPER_DIR="src/decimo/gmp" local WRAPPER_LIB if [[ "$(uname)" == "Darwin" ]]; then @@ -111,7 +111,7 @@ run_bigfloat() { run_cli() { # CLI tests need the extra -I src/cli include path - ensure_decimo_mojopkg + ensure_decimo_package for f in tests/cli/*.mojo; do pixi run mojo run -I tests -I src/cli -D ASSERT=all --debug-level=full "$f" done