diff --git a/.gitignore b/.gitignore index 235d5309..f257ff9d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ local # Python build artifacts *.so __pycache__/ -*.pyc \ No newline at end of file +*.pyc +python/dist/ \ No newline at end of file diff --git a/docs/plans/mojo4py.md b/docs/plans/mojo4py.md index 87dfe8ae..9ef4db5f 100644 --- a/docs/plans/mojo4py.md +++ b/docs/plans/mojo4py.md @@ -116,15 +116,23 @@ The binding code lives in a top-level `python/` directory at the project root, p ```txt python/ -├── decimo_module.mojo ← Mojo binding source (builds to _decimo.so) -├── _decimo.so ← compiled extension (gitignored) -├── decimo.py ← Python wrapper: Decimal class + BigDecimal alias +├── pyproject.toml ← PyPI package config (hatchling, src layout) +├── README.md ← PyPI landing page +├── decimo_module.mojo ← Mojo binding source (builds to src/decimo/_decimo.so) +├── src/ +│ └── decimo/ +│ ├── __init__.py ← Python wrapper: Decimal class + BigDecimal alias +│ ├── _decimo.pyi ← Type stub for Pylance/mypy +│ ├── _decimo.so ← compiled extension (gitignored) +│ └── py.typed ← PEP 561 marker └── tests/ └── test_decimo.py ← Python tests ``` The core Mojo library (`src/decimo/`) is not modified — all binding logic lives in `python/decimo_module.mojo` as free functions. +The `src` layout (PEP 517) is used so that `pip install -e python/` installs cleanly and the package is importable as `from decimo import Decimal` without any path manipulation. + ### 4.1 Two-Layer Architecture Due to CPython slot limitations (see Phase 0 findings in Section 8), a two-layer pattern is used: @@ -136,11 +144,11 @@ This keeps the core `BigDecimal` struct unmodified and provides full Pythonic be ### 4.2 The `Decimal` Alias -The `Decimal` alias is set in `decimo.py` as the primary class name, with `BigDecimal = Decimal` for users who prefer the full name: +The `Decimal` alias is set in `src/decimo/__init__.py` as the primary class name, with `BigDecimal = Decimal` for users who prefer the full name: ```python -# python/decimo.py -from _decimo import BigDecimal as _BigDecimal +# python/src/decimo/__init__.py +from ._decimo import BigDecimal as _BigDecimal class Decimal: __slots__ = ("_inner",) @@ -164,17 +172,20 @@ from decimo import BigDecimal # also works, same class ## 5. Build System Integration (pixi.toml) -Tasks added to `pixi.toml`: +Tasks in `pixi.toml`: ```toml # python bindings (mojo4py) -pybuild = "pixi run mojo build python/decimo_module.mojo --emit shared-lib -I src -o python/_decimo.so" -pytest = "pixi run pybuild && pixi run python python/tests/test_decimo.py" +bpy = "clear && pixi run buildpy" +buildpy = "pixi run mojo build python/decimo_module.mojo --emit shared-lib -I src -o python/src/decimo/_decimo.so" +testpy = "pixi run buildpy && pixi run python python/tests/test_decimo.py" +tpy = "clear && pixi run testpy" +wheel = "cd python && pixi run python -m build --wheel" ``` -- `pixi run pybuild` — compiles the Mojo binding to `python/_decimo.so`. -- `pixi run pytest` — builds then runs the Python test suite. -- The `-I src` flag ensures `import decimo` in the Mojo binding resolves to `src/decimo/`. No need to pre-package `decimo.mojopkg`. +- `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 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. --- @@ -335,9 +346,14 @@ jobs: - [x] Manually build the `.so` with `mojo build --emit shared-lib -I src`. - [x] Import from Python, confirm round-trip: `str(Decimal("1.23")) == "1.23"`. - [x] Identify trait gaps (`Representable`, `Movable`, etc. — all satisfied). -- [x] Arithmetic: `+`, `-`, `*` work. Comparison: `==`, `<`, `<=`, `>`, `>=`, `!=` work. +- [x] Arithmetic: `+`, `-`, `*`, `/` work. Comparison: `==`, `<`, `<=`, `>`, `>=`, `!=` work. - [x] `Decimal` alias (`Decimal is BigDecimal` → `True` in Python). - [x] Large arbitrary-precision numbers work (38+ digit numbers). +- [x] Cross-validated all operations against Python stdlib `decimal.Decimal`. +- [x] Type stubs (`_decimo.pyi`) and `py.typed` PEP 561 marker. +- [x] `pyproject.toml` + `src` layout for PyPI; placeholder wheel built and uploaded to PyPI. +- [x] CI: `test-python` job in GitHub Actions parallel CI. +- [x] Python code formatter: `ruff` integrated into `pixi run format` and pre-commit. **Phase 0 findings & architecture decisions:** @@ -391,12 +407,13 @@ jobs: ### Phase 4 — Packaging + Distribution -- [ ] Create `python/` directory with `pyproject.toml` and `__init__.py`. -- [ ] Write `.pyi` stubs for all types. -- [ ] Add `py.typed` marker (PEP 561). -- [ ] Test `pip install` of the built wheel locally. +- [x] Create `python/` directory with `pyproject.toml` (hatchling, src layout) and `src/decimo/__init__.py`. +- [x] Write `.pyi` stubs for `BigDecimal` (`_decimo.pyi`). +- [x] Add `py.typed` marker (PEP 561). +- [x] PyPI name reserved — placeholder wheel (`0.1.0.dev0`) published to PyPI. +- [ ] Test `pip install` of the built wheel locally (blocked until pre-built `.so` in wheel). - [ ] Set up GitHub Actions for wheel builds (macOS arm64, Linux x86_64). -- [ ] Publish to PyPI (or TestPyPI first). +- [ ] Publish platform-specific wheels with bundled `.so`. ### Phase 5 — Ergonomics + Stabilization diff --git a/pixi.lock b/pixi.lock index 82bdb56d..0e29d3f2 100644 --- a/pixi.lock +++ b/pixi.lock @@ -13,14 +13,37 @@ environments: - 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.1.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/linux-64/backports.zstd-1.3.0-py313h18e8e13_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py313hf159716_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py313h07c4f96_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.12-py313hd8ed1ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py313heb322e3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyha804496_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda @@ -29,7 +52,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda @@ -38,43 +63,82 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.modular.com/max-nightly/noarch/mblack-26.1.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.modular.com/max-nightly/linux-64/mojo-0.26.1.0-release.conda - conda: https://conda.modular.com/max-nightly/linux-64/mojo-compiler-0.26.1.0-release.conda - conda: https://conda.modular.com/max-nightly/noarch/mojo-python-0.26.1.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.0-pyhcf101f3_1.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.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.3.3-py310h6de7dc8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.0.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.12-hc97d973_100_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.4.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.12-h4df99d1_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.15.2-h40fa522_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.4.1-py313h78bf25f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py313h07c4f96_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - 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.1.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/osx-arm64/backports.zstd-1.3.0-py313h48bb75e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py313hde1f3bb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py313h224173a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py313hcdf3177_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.12-py313hd8ed1ab_100.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-h38cb7af_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyh534df25_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda @@ -85,31 +149,49 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.modular.com/max-nightly/noarch/mblack-26.1.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.modular.com/max-nightly/osx-arm64/mojo-0.26.1.0-release.conda - conda: https://conda.modular.com/max-nightly/osx-arm64/mojo-compiler-0.26.1.0-release.conda - conda: https://conda.modular.com/max-nightly/noarch/mojo-python-0.26.1.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.0-pyhcf101f3_1.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.5-h5e97a16_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.3-py310hf32026f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.0.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.12-h20e6be0_100_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.4.0-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.12-h4df99d1_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.15.2-h279115b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py313h6535dbc_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h888dc83_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 @@ -150,6 +232,80 @@ packages: license: Apache-2.0 size: 1245438 timestamp: 1772049389471 +- conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda + sha256: e1c3dc8b5aa6e12145423fed262b4754d70fec601339896b9ccf483178f690a6 + md5: 767d508c1a67e02ae8f50e44cacfadb2 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 7069 + timestamp: 1733218168786 +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda + sha256: 25abdb37e186f0d6ac3b774a63c81c5bc4bf554b5096b51343fa5e7c381193b1 + md5: bea46844deb274b2cc2a3a941745fa73 + depends: + - python >=3.10 + - backports + - python + license: MIT + license_family: MIT + size: 35739 + timestamp: 1767290467820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py313h18e8e13_0.conda + sha256: 9552afbec37c4d8d0e83a5c4c6b3c7f4b8785f935094ce3881e0a249045909ce + md5: d9e90792551a527200637e23a915dd79 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.13.* *_cp313 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause AND MIT AND EPL-2.0 + size: 240943 + timestamp: 1767044981366 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.3.0-py313h48bb75e_0.conda + sha256: f3047ca3b41bb444b4b5a71a6eee182623192c77019746dd4685fd260becb249 + md5: 54008c5cc8928e5cb5a0f9206b829451 + depends: + - python + - python 3.13.* *_cp313 + - __osx >=11.0 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.13.* *_cp313 + license: BSD-3-Clause AND MIT AND EPL-2.0 + size: 244371 + timestamp: 1767045003420 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py313hf159716_1.conda + sha256: dadec2879492adede0a9af0191203f9b023f788c18efd45ecac676d424c458ae + md5: 6c4d3597cf43f3439a51b2b13e29a4ba + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.2.0 hb03c661_1 + license: MIT + license_family: MIT + size: 367721 + timestamp: 1764017371123 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py313hde1f3bb_1.conda + sha256: 2e21dccccd68bedd483300f9ab87a425645f6776e6e578e10e0dd98c946e1be9 + md5: b03732afa9f4f54634d94eb920dfb308 + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + constrains: + - libbrotlicommon 1.2.0 hc919400_1 + license: MIT + license_family: MIT + size: 359568 + timestamp: 1764018359470 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 md5: d2ffd7602c02f2b316fd921d39876885 @@ -177,6 +333,51 @@ packages: license: ISC size: 147413 timestamp: 1772006283803 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda + sha256: a6b118fd1ed6099dc4fc03f9c492b88882a780fadaef4ed4f93dc70757713656 + md5: 765c4d97e877cdbbb88ff33152b86125 + depends: + - python >=3.10 + license: ISC + size: 151445 + timestamp: 1772001170301 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda + sha256: 2162a91819945c826c6ef5efe379e88b1df0fe9a387eeba23ddcf7ebeacd5bd6 + md5: d0616e7935acab407d1543b28c446f6f + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 298357 + timestamp: 1761202966461 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py313h224173a_1.conda + sha256: 1fa69651f5e81c25d48ac42064db825ed1a3e53039629db69f86b952f5ce603c + md5: 050374657d1c7a4f2ea443c0d0cbd9a0 + depends: + - __osx >=11.0 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 291376 + timestamp: 1761203583358 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + sha256: b32f8362e885f1b8417bac2b3da4db7323faa12d5db62b7fd6691c02d60d6f59 + md5: a22d1fd9bf98827e280a02875d9a007a + depends: + - python >=3.10 + license: MIT + license_family: MIT + size: 50965 + timestamp: 1760437331772 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda sha256: 38cfe1ee75b21a8361c8824f5544c3866f303af1762693a178266d7f198e8715 md5: ea8a6c3256897cc31263de9f455e25d9 @@ -188,6 +389,41 @@ packages: license_family: BSD size: 97676 timestamp: 1764518652276 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py313h07c4f96_1.conda + sha256: 8edab14df6259d9678c83ea876a56902da44b50022cda8789c706d4d5336a4b9 + md5: d57661f0421c6fdb9a617280e633e4c8 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.0.0 + - libgcc >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 142385 + timestamp: 1760363028602 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py313hcdf3177_1.conda + sha256: 22ef41f6533fb1241097d4e9db11bbc0191b8a9fd0543cd85dcb56a197191b34 + md5: 49f95432e803116f993f2e680a88bdf3 + depends: + - __osx >=11.0 + - cffi >=1.0.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + size: 115969 + timestamp: 1760363255005 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.12-py313hd8ed1ab_100.conda noarch: generic sha256: 7636809bda35add7af66cda1fee156136fcba0a1e24bbef1d591ee859df755a8 @@ -198,6 +434,73 @@ packages: license: Python-2.0 size: 48648 timestamp: 1770270374831 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.5-py313heb322e3_0.conda + sha256: 553f4ee18ad755d690ad63fa8e00d89598ecc4945ec046a8af808ddee5bb1ca0 + md5: 964f25e322b16cae073da8f5b7adf123 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.14 + - libgcc >=14 + - openssl >=3.5.5,<4.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - __glibc >=2.17 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + size: 1718868 + timestamp: 1770772833949 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda + sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 + md5: ce96f2f470d39bd96ce03945af92e280 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - libglib >=2.86.2,<3.0a0 + - libexpat >=2.7.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + size: 447649 + timestamp: 1764536047944 +- conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + sha256: 0d605569a77350fb681f9ed8d357cc71649b59a304099dc9d09fbeec5e84a65e + md5: d6bd3cd217e62bbd7efe67ff224cd667 + depends: + - python >=3.10 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + size: 438002 + timestamp: 1766092633160 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.1,<5 + - python + license: MIT + license_family: MIT + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17397 + timestamp: 1737618427549 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda sha256: 142a722072fa96cf16ff98eaaf641f54ab84744af81754c292cb81e0881c0329 md5: 186a18e3ba246eccfc7cff00cd19a870 @@ -218,6 +521,26 @@ packages: license_family: MIT size: 12358010 timestamp: 1767970350308 +- conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda + sha256: 54c80a4ca6e6a19b4bb89c829f757d0de00362a3bfa4647517d2ebd519717f0f + md5: 563a022fc58cf7a200c35cb3fee07a6b + depends: + - python >=3.10 + - urllib3 >=2,<3 + - python + license: Apache-2.0 + license_family: APACHE + size: 27972 + timestamp: 1770237711404 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 + md5: 53abe63df7e10a6ba605dc5f9f961d36 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + size: 50721 + timestamp: 1760286526795 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 md5: 63ccfdc3a3ce25b027b8767eb722fca8 @@ -229,6 +552,60 @@ packages: license_family: APACHE size: 34641 timestamp: 1747934053147 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda + sha256: acc1d991837c0afb67c75b77fdc72b4bf022aac71fedd8b9ea45918ac9b08a80 + md5: c85c76dc67d75619a92f51dfbce06992 + depends: + - python >=3.9 + - zipp >=3.1.0 + constrains: + - importlib-resources >=6.5.2,<6.5.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 33781 + timestamp: 1736252433366 +- conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda + sha256: 3cc991f0f09dfd00d2626e745ba68da03e4f1dcbb7b36dd20f7a7373643cd5d5 + md5: d59568bad316413c89831456e691de29 + depends: + - python >=3.10 + - more-itertools + - python + license: MIT + license_family: MIT + size: 14831 + timestamp: 1767294269456 +- conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.0-pyhcf101f3_0.conda + sha256: 04c9f919dcc9edd18f748c47d809479812429af27c43c5562a861df22d5bda6a + md5: f34ec3aa0ea911a038d973d97603faf3 + depends: + - python >=3.10 + - backports.tarfile + - python + license: MIT + license_family: MIT + size: 15566 + timestamp: 1768299702258 +- conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhcf101f3_1.conda + sha256: 6a91447b3bb4d7ae94cc0d77ed12617796629aee11111efe7ea43cbd0e113bda + md5: aa83cc08626bf6b613a3103942be8951 + depends: + - python >=3.10 + - more-itertools + - python + license: MIT + license_family: MIT + size: 18744 + timestamp: 1767294193246 +- conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda + sha256: 00d37d85ca856431c67c8f6e890251e7cc9e5ef3724a0302b8d4a101f22aa27f + md5: b4b91eb14fbe2f850dd2c5fc20676c0d + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 40015 + timestamp: 1740828380668 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a md5: 4ebae00eae9705b0c3d6d1018a81d047 @@ -260,6 +637,38 @@ packages: license_family: BSD size: 65503 timestamp: 1760643864586 +- conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyh534df25_0.conda + sha256: 9def5c6fb3b3b4952a4f6b55a019b5c7065b592682b84710229de5a0b73f6364 + md5: c88f9579d08eb4031159f03640714ce3 + depends: + - __osx + - importlib-metadata >=4.11.4 + - importlib_resources + - jaraco.classes + - jaraco.context + - jaraco.functools + - python >=3.10 + license: MIT + license_family: MIT + size: 37924 + timestamp: 1763320995459 +- conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyha804496_0.conda + sha256: 010718b1b1a35ce72782d38e6d6b9495d8d7d0dbea9a3e42901d030ff2189545 + md5: 9eeb0eaf04fa934808d3e070eebbe630 + depends: + - __linux + - importlib-metadata >=4.11.4 + - importlib_resources + - jaraco.classes + - jaraco.context + - jaraco.functools + - jeepney >=0.4.2 + - python >=3.10 + - secretstorage >=3.2 + license: MIT + license_family: MIT + size: 37717 + timestamp: 1763320674488 - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 md5: b38117a3c920364aff79f870c984b4a3 @@ -404,6 +813,21 @@ packages: license_family: GPL size: 27526 timestamp: 1771378224552 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda + sha256: a27e44168a1240b15659888ce0d9b938ed4bdb49e9ea68a7c1ff27bcea8b55ce + md5: bb26456332b07f68bf3b7622ed71c0da + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - glib 2.86.4 *_1 + license: LGPL-2.1-or-later + size: 4398701 + timestamp: 1771863239578 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda sha256: 21337ab58e5e0649d869ab168d4e609b033509de22521de1bfed0c031bfc5110 md5: 239c5e9546c38a1e884d69effcf4c882 @@ -413,6 +837,15 @@ packages: license_family: GPL size: 603262 timestamp: 1771378117851 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + size: 790176 + timestamp: 1754908768807 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb md5: c7c83eecbb72d88b940c249af56c8b17 @@ -544,6 +977,16 @@ packages: license_family: Other size: 46438 timestamp: 1727963202283 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e + md5: 5b5203189eb668f042ac2b0826244964 + depends: + - mdurl >=0.1,<1 + - python >=3.10 + license: MIT + license_family: MIT + size: 64736 + timestamp: 1754951288511 - conda: https://conda.modular.com/max-nightly/noarch/mblack-26.1.0-release.conda noarch: python sha256: 6ccec52fe7354f44be93a41a122d2214ecdb030e6362afe8e7876eab35472e62 @@ -559,6 +1002,15 @@ packages: license: MIT size: 135743 timestamp: 1769478151312 +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 - conda: https://conda.modular.com/max-nightly/linux-64/mojo-0.26.1.0-release.conda sha256: e945e8fbff0fdd2064ea193b26fb4ba95ab782367cfd2a2c4522350066434494 depends: @@ -601,6 +1053,16 @@ packages: license: LicenseRef-Modular-Proprietary size: 24169 timestamp: 1769478151311 +- conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.0-pyhcf101f3_1.conda + sha256: 449609f0d250607a300754474350a3b61faf45da183d3071e9720e453c765b8a + md5: 32f78e9d06e8593bc4bbf1338da06f5f + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 69210 + timestamp: 1764487059562 - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda sha256: 6ed158e4e5dd8f6a10ad9e525631e35cee8557718f83de7a4e3966b1f772c4b1 md5: e9c622e0d00fa24a6292279af3ab6d06 @@ -627,6 +1089,37 @@ packages: license: X11 AND BSD-3-Clause size: 797030 timestamp: 1738196177597 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.3.3-py310h6de7dc8_0.conda + noarch: python + sha256: dcc0eee49226ef2f8f58de541a1b0ec492f4f0928ec43b1c26bf498511c363b1 + md5: 5abaf4ccde7924f6f49b36e5fca892a1 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 678551 + timestamp: 1771078193635 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.3-py310hf32026f_0.conda + noarch: python + sha256: 4b2af5ca6f5d578f2eceaf8025e8bd80d3f7e5f932fa5ff2f2b4a039aef8d784 + md5: 2766e89e8aa0a79626a735d4afdeefcd + depends: + - python + - __osx >=11.0 + - _python_abi3_support 1.* + - cpython >=3.10 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 634553 + timestamp: 1771078287944 - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda sha256: 44c877f8af015332a5d12f5ff0fb20ca32f896526a7d0cdb30c769df1144fb5c md5: f61eb8cd60ff9057122a3d338b99c00f @@ -667,6 +1160,18 @@ packages: license_family: MOZILLA size: 53739 timestamp: 1769677743677 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda + sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff + md5: 7a3bff861a6583f1889021facefc08b1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 1222481 + timestamp: 1763655398280 - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.2-pyhcf101f3_0.conda sha256: 7f263219cecf0ba6d74c751efa60c4676ce823157ca90aa43ebba5ac615ca0fa md5: 4fefefb892ce9cc1539405bec2f1a6cd @@ -677,6 +1182,45 @@ packages: license_family: MIT size: 25643 timestamp: 1771233827084 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a + md5: 6b6ece66ebcae2d5f326c77ef2c5a066 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 889287 + timestamp: 1750615908735 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyproject_hooks-1.2.0-pyhd8ed1ab_1.conda + sha256: 065ac44591da9abf1ff740feb25929554b920b00d09287a551fcced2c9791092 + md5: d4582021af437c931d7d77ec39007845 + depends: + - python >=3.9 + - tomli >=1.1.0 + license: MIT + license_family: MIT + size: 15528 + timestamp: 1733710122949 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.12-hc97d973_100_cp313.conda build_number: 100 sha256: 8a08fe5b7cb5a28aa44e2994d18dbf77f443956990753a4ca8173153ffb6eb56 @@ -726,6 +1270,22 @@ packages: size: 12770674 timestamp: 1770272314517 python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/noarch/python-build-1.4.0-pyh332efcf_0.conda + sha256: 195e483a12bcec40b817f4001d4d4b8ea1cb2de66a62aeabfff6e32e29b3f407 + md5: dbbb75958b0b03842dcf9be2f200fc10 + depends: + - colorama + - importlib-metadata >=4.6 + - packaging >=19.0 + - pyproject_hooks + - python >=3.10 + - tomli >=1.1.0 + constrains: + - build <0 + license: MIT + license_family: MIT + size: 26687 + timestamp: 1767988747352 - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 md5: 5b8d21249ff20967101ffa321cab24e8 @@ -808,6 +1368,67 @@ packages: license_family: GPL size: 313930 timestamp: 1765813902568 +- conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda + sha256: 66f3adf6aaabf977cfcc22cb65607002b1de4a22bc9fac7be6bb774bc6f85a3a + md5: c58dd5d147492671866464405364c0f1 + depends: + - cmarkgfm >=0.8.0 + - docutils >=0.21.2 + - nh3 >=0.2.14 + - pygments >=2.5.1 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 17481 + timestamp: 1734339765256 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda + sha256: 7813c38b79ae549504b2c57b3f33394cea4f2ad083f0994d2045c2e24cb538c5 + md5: c65df89a0b2e321045a9e01d1337b182 + depends: + - python >=3.10 + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - urllib3 >=1.21.1,<3 + - python + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 63602 + timestamp: 1766926974520 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda + sha256: c0b815e72bb3f08b67d60d5e02251bbb0164905b5f72942ff5b6d2a339640630 + md5: 66de8645e324fda0ea6ef28c2f99a2ab + depends: + - python >=3.9 + - requests >=2.0.1,<3.0.0 + license: Apache-2.0 + license_family: APACHE + size: 44285 + timestamp: 1733734886897 +- conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda + sha256: d617373ba1a5108336cb87754d030b9e384dcf91796d143fa60fe61e76e5cfb0 + md5: 43e14f832d7551e5a8910672bfc3d8c6 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 38028 + timestamp: 1733921806657 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda + sha256: b06ce84d6a10c266811a7d3adbfa1c11f13393b91cc6f8a5b468277d90be9590 + md5: 7a6289c50631d620652f5045a63eb573 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.10 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + size: 208472 + timestamp: 1771572730357 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.15.2-h40fa522_0.conda noarch: python sha256: e0403324ac0de06f51c76ae2a4671255d48551a813d1f1dc03bd4db7364604f0 @@ -835,6 +1456,19 @@ packages: license_family: MIT size: 8415205 timestamp: 1771570140500 +- conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.4.1-py313h78bf25f_0.conda + sha256: 43ea89b53cbede879e57ac9dd20153c5cd2bb9575228e7faf5a8764aa6c201b7 + md5: 013a7d73eaef154f0dc5e415ffa8ff87 + depends: + - cryptography >=2.0 + - dbus + - jeepney >=0.6 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: BSD-3-Clause + license_family: BSD + size: 32933 + timestamp: 1763045369115 - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d md5: 3339e3b65d58accf4ca4fb8748ab16b3 @@ -911,12 +1545,55 @@ packages: license_family: BSD size: 110051 timestamp: 1733367480074 +- conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda + sha256: 0370098cab22773e33755026bf78539c2f05645fce7dcc9713d01e21950756bb + md5: 901a86453fa6183e914b937643619a03 + depends: + - id + - importlib-metadata >=3.6 + - keyring >=21.2.0 + - packaging >=24.0 + - python >=3.10 + - readme_renderer >=35.0 + - requests >=2.20 + - requests-toolbelt >=0.8.0,!=0.9.0 + - rfc3986 >=1.4.0 + - rich >=12.0.0 + - urllib3 >=1.26.0 + - python + license: Apache-2.0 + license_family: APACHE + size: 42488 + timestamp: 1757013705407 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + size: 51692 + timestamp: 1756220668932 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c md5: ad659d0a2b3e47e38d829aa8cad2d610 license: LicenseRef-Public-Domain size: 119135 timestamp: 1767016325805 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda + sha256: af641ca7ab0c64525a96fd9ad3081b0f5bcf5d1cbb091afb3f6ed5a9eee6111a + md5: 9272daa869e03efe68833e3dc7a02130 + depends: + - backports.zstd >=1.0.0 + - brotli-python >=1.2.0 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.10 + license: MIT + license_family: MIT + size: 103172 + timestamp: 1767817860341 - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda sha256: 47cfe31255b91b4a6fa0e9dbaf26baa60ac97e033402dbc8b90ba5fee5ffe184 md5: 8035e5b54c08429354d5d64027041cad @@ -963,3 +1640,13 @@ packages: license_family: BSD size: 601375 timestamp: 1764777111296 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + sha256: 9485ba49e8f47d2b597dd399e88f4802e100851b27c21d7525625b0b4025a5d9 + md5: ab136e4c34e97f34fb621d2592a393d8 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 433413 + timestamp: 1764777166076 diff --git a/pixi.toml b/pixi.toml index 4284102c..808a12ab 100644 --- a/pixi.toml +++ b/pixi.toml @@ -12,7 +12,9 @@ version = "0.8.0" argmojo = ">=0.1.0,<0.2" # CLI argument parsing for the Decimo calculator mojo = "==0.26.1" # Mojo language compiler and runtime python = ">=3.13,<3.14" # For Python bindings and tests +python-build = ">=1.4.0,<2" # Build PyPI wheel (`pixi run wheel`) ruff = ">=0.9,<1" # Python code formatter +twine = ">=6.2.0,<7" # Upload wheel to PyPI (`twine upload dist/*`) [tasks] # format the code @@ -82,6 +84,8 @@ testcli = "bash tests/test_cli.sh" # python bindings (mojo4py) bpy = "clear && pixi run buildpy" buildpy = """pixi run mojo build python/decimo_module.mojo \ ---emit shared-lib -I src -o python/_decimo.so""" +--emit shared-lib -I src -o python/src/decimo/_decimo.so""" testpy = "pixi run buildpy && pixi run python python/tests/test_decimo.py" tpy = "clear && pixi run testpy" +# build PyPI wheel (placeholder, no .so included) +wheel = "cd python && pixi run python -m build --wheel" diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000..2677b71c --- /dev/null +++ b/python/README.md @@ -0,0 +1,62 @@ +# decimo + +**Arbitrary-precision decimal and integer arithmetic for Python, powered by Mojo.** + +[![PyPI](https://img.shields.io/pypi/v/decimo)](https://pypi.org/project/decimo/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/forfudan/decimo/blob/main/LICENSE) + +> ⚠️ **Pre-Alpha / Placeholder release.** +> The Python bindings are under active development. A proper installable wheel is coming soon. +> Full Mojo library is already available — see the [main repository](https://github.com/forfudan/decimo). + +--- + +## What is decimo? + +`decimo` is an arbitrary-precision decimal and integer library, originally written in [Mojo](https://www.modular.com/mojo). +This package exposes `decimo`'s `BigDecimal` type to Python via a Mojo-built CPython extension module (`_decimo.so`), +with a thin Python wrapper providing full Pythonic operator support. + +```python +from decimo import Decimal + +a = Decimal("1.234567890123456789012345678901234567890") +b = Decimal("9.876543210987654321098765432109876543210") + +print(a + b) # 11.111111101111111110111111111111111111100 +print(a * b) # 12.193263111263526900... +print(a / b) # 0.12499999... +``` + +## Status + +| Feature | Status | +|---|---| +| `Decimal` (BigDecimal) arithmetic (`+`, `-`, `*`, `/`) | ✅ Working | +| Comparison operators | ✅ Working | +| Unary `-`, `abs()`, `bool()` | ✅ Working | +| Pre-built wheels on PyPI | 🚧 Coming soon | +| `BigInt` / `Decimal128` Python bindings | 🔜 Planned | + +## Building from source + +The extension requires [Mojo](https://docs.modular.com/mojo/manual/get-started/) and [pixi](https://pixi.sh): + +```bash +git clone https://github.com/forfudan/decimo +cd decimo +pixi run buildpy +# .so is built at python/src/decimo/_decimo.so +``` + +Then install in editable mode: + +```bash +pip install -e python/ +``` + +## Links + +- **GitHub**: +- **Changelog**: +- **Mojo library docs**: diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 00000000..d86ffd8c --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,40 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/decimo"] + +[project] +name = "decimo" +version = "0.1.0.dev0" +description = "Arbitrary-precision decimal and integer arithmetic for Python, powered by Mojo" +readme = "README.md" +license = { text = "Apache-2.0" } +authors = [{ name = "ZHU Yuhao (朱宇浩)", email = "dr.yuhao.zhu@outlook.com" }] +requires-python = ">=3.11" +keywords = [ + "decimal", + "arbitrary-precision", + "mojo", + "bigdecimal", + "arithmetic", + "numeric", +] +classifiers = [ + "Development Status :: 2 - Pre-Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering :: Mathematics", + "Typing :: Typed", +] + +[project.urls] +Homepage = "https://github.com/forfudan/decimo" +Repository = "https://github.com/forfudan/decimo" +Issues = "https://github.com/forfudan/decimo/issues" +Changelog = "https://github.com/forfudan/decimo/blob/main/docs/changelog.md" diff --git a/python/decimo.py b/python/src/decimo/__init__.py similarity index 88% rename from python/decimo.py rename to python/src/decimo/__init__.py index 8ec75594..3953f69b 100644 --- a/python/decimo.py +++ b/python/src/decimo/__init__.py @@ -8,7 +8,20 @@ print(a + b) # 3.8 """ -from _decimo import BigDecimal as _BigDecimal +__version__ = "0.1.0.dev0" +__all__ = ["Decimal", "BigDecimal"] + +try: + from ._decimo import BigDecimal as _BigDecimal +except ImportError as _err: + raise ImportError( + "decimo requires a compiled Mojo extension (_decimo native module).\n" + "This package does not yet include pre-built wheels.\n" + "Build from source:\n" + " git clone https://github.com/forfudan/decimo && cd decimo\n" + " pixi run buildpy\n" + " pip install -e python/\n" + ) from _err class Decimal: diff --git a/python/_decimo.pyi b/python/src/decimo/_decimo.pyi similarity index 100% rename from python/_decimo.pyi rename to python/src/decimo/_decimo.pyi diff --git a/python/src/decimo/py.typed b/python/src/decimo/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/python/tests/test_decimo.py b/python/tests/test_decimo.py index 826b6726..1669d258 100644 --- a/python/tests/test_decimo.py +++ b/python/tests/test_decimo.py @@ -8,8 +8,8 @@ from pathlib import Path import sys -# Add python/ directory to sys.path so `import decimo` resolves to decimo.py -sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) +# Add python/src/ to sys.path so `import decimo` resolves to src/decimo/__init__.py +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src")) import decimo