rustylab 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. rustylab-0.3.0/.claude/settings.json +8 -0
  2. rustylab-0.3.0/.forgejo/workflows/num_acc_testing.yml +46 -0
  3. rustylab-0.3.0/.github/workflows/build-wheels.yml +122 -0
  4. rustylab-0.3.0/.gitignore +76 -0
  5. rustylab-0.3.0/.vscode/ltex.dictionary.en-GB.txt +6 -0
  6. rustylab-0.3.0/.vscode/ltex.hiddenFalsePositives.en-GB.txt +7 -0
  7. rustylab-0.3.0/CLAUDE.md +22 -0
  8. rustylab-0.3.0/CONTRIBUTING.md +122 -0
  9. rustylab-0.3.0/Cargo.lock +2399 -0
  10. rustylab-0.3.0/Cargo.toml +25 -0
  11. rustylab-0.3.0/LICENSE +9 -0
  12. rustylab-0.3.0/PKG-INFO +128 -0
  13. rustylab-0.3.0/README.md +103 -0
  14. rustylab-0.3.0/RELEASING.md +280 -0
  15. rustylab-0.3.0/benchmarks/bench_misc.ipynb +245 -0
  16. rustylab-0.3.0/benchmarks/bench_misc.md +182 -0
  17. rustylab-0.3.0/benchmarks/bench_optimize.ipynb +208 -0
  18. rustylab-0.3.0/benchmarks/bench_optimize.md +155 -0
  19. rustylab-0.3.0/benchmarks/bench_special.ipynb +810 -0
  20. rustylab-0.3.0/benchmarks/bench_special.md +607 -0
  21. rustylab-0.3.0/benchmarks/bench_stats_prob_dists.ipynb +3286 -0
  22. rustylab-0.3.0/benchmarks/bench_stats_prob_dists.md +3125 -0
  23. rustylab-0.3.0/benchmarks/export_notebooks.sh +19 -0
  24. rustylab-0.3.0/benchmarks/requirements-benchmarking.txt +35 -0
  25. rustylab-0.3.0/docs/.gitignore +2 -0
  26. rustylab-0.3.0/docs/.vscode/ltex.dictionary.en-GB.txt +29 -0
  27. rustylab-0.3.0/docs/.vscode/ltex.dictionary.en-US.txt +1 -0
  28. rustylab-0.3.0/docs/.vscode/ltex.hiddenFalsePositives.en-GB.txt +40 -0
  29. rustylab-0.3.0/docs/.vscode/ltex.hiddenFalsePositives.en-US.txt +1 -0
  30. rustylab-0.3.0/docs/Home.qmd +346 -0
  31. rustylab-0.3.0/docs/_quarto.yml +6 -0
  32. rustylab-0.3.0/docs/misc.qmd +111 -0
  33. rustylab-0.3.0/docs/optimize.qmd +64 -0
  34. rustylab-0.3.0/docs/special.qmd +799 -0
  35. rustylab-0.3.0/docs/stats/bernoulli.qmd +576 -0
  36. rustylab-0.3.0/docs/stats/beta.qmd +595 -0
  37. rustylab-0.3.0/docs/stats/binom.qmd +597 -0
  38. rustylab-0.3.0/docs/stats/cauchy.qmd +565 -0
  39. rustylab-0.3.0/docs/stats/chi2.qmd +586 -0
  40. rustylab-0.3.0/docs/stats/expon.qmd +565 -0
  41. rustylab-0.3.0/docs/stats/f.qmd +591 -0
  42. rustylab-0.3.0/docs/stats/gamma.qmd +584 -0
  43. rustylab-0.3.0/docs/stats/norm.qmd +575 -0
  44. rustylab-0.3.0/docs/stats/poisson.qmd +574 -0
  45. rustylab-0.3.0/docs/stats/randint.qmd +575 -0
  46. rustylab-0.3.0/docs/stats/t.qmd +580 -0
  47. rustylab-0.3.0/docs/stats/uniform.qmd +564 -0
  48. rustylab-0.3.0/docs/stats.qmd +31 -0
  49. rustylab-0.3.0/images/avatar.png +0 -0
  50. rustylab-0.3.0/images/banner.jpg +0 -0
  51. rustylab-0.3.0/pyproject.toml +39 -0
  52. rustylab-0.3.0/python/rustylab/__init__.py +3 -0
  53. rustylab-0.3.0/python/rustylab/expr/__init__.py +3 -0
  54. rustylab-0.3.0/python/rustylab/expr/misc.py +15 -0
  55. rustylab-0.3.0/python/rustylab/expr/special.py +138 -0
  56. rustylab-0.3.0/python/rustylab/expr/stats/__init__.py +17 -0
  57. rustylab-0.3.0/python/rustylab/expr/stats/_common.py +25 -0
  58. rustylab-0.3.0/python/rustylab/expr/stats/bernoulli.py +125 -0
  59. rustylab-0.3.0/python/rustylab/expr/stats/beta.py +126 -0
  60. rustylab-0.3.0/python/rustylab/expr/stats/binom.py +127 -0
  61. rustylab-0.3.0/python/rustylab/expr/stats/cauchy.py +128 -0
  62. rustylab-0.3.0/python/rustylab/expr/stats/chi2.py +113 -0
  63. rustylab-0.3.0/python/rustylab/expr/stats/expon.py +125 -0
  64. rustylab-0.3.0/python/rustylab/expr/stats/f.py +126 -0
  65. rustylab-0.3.0/python/rustylab/expr/stats/gamma.py +127 -0
  66. rustylab-0.3.0/python/rustylab/expr/stats/norm.py +125 -0
  67. rustylab-0.3.0/python/rustylab/expr/stats/poisson.py +125 -0
  68. rustylab-0.3.0/python/rustylab/expr/stats/randint.py +127 -0
  69. rustylab-0.3.0/python/rustylab/expr/stats/t.py +113 -0
  70. rustylab-0.3.0/python/rustylab/expr/stats/uniform.py +125 -0
  71. rustylab-0.3.0/requirements.txt +3 -0
  72. rustylab-0.3.0/src/constants.rs +366 -0
  73. rustylab-0.3.0/src/lib.rs +62 -0
  74. rustylab-0.3.0/src/misc/fsum.rs +680 -0
  75. rustylab-0.3.0/src/misc/mod.rs +18 -0
  76. rustylab-0.3.0/src/optimize/mod.rs +19 -0
  77. rustylab-0.3.0/src/optimize/newton.rs +323 -0
  78. rustylab-0.3.0/src/optimize/results.rs +138 -0
  79. rustylab-0.3.0/src/pyerr.rs +108 -0
  80. rustylab-0.3.0/src/special/beta.rs +94 -0
  81. rustylab-0.3.0/src/special/betainc.rs +2643 -0
  82. rustylab-0.3.0/src/special/betaincc.rs +136 -0
  83. rustylab-0.3.0/src/special/betainccinv.rs +122 -0
  84. rustylab-0.3.0/src/special/betaincinv.rs +373 -0
  85. rustylab-0.3.0/src/special/erf.rs +277 -0
  86. rustylab-0.3.0/src/special/erfc.rs +277 -0
  87. rustylab-0.3.0/src/special/factorial.rs +148 -0
  88. rustylab-0.3.0/src/special/gamma.rs +191 -0
  89. rustylab-0.3.0/src/special/gammainc.rs +136 -0
  90. rustylab-0.3.0/src/special/gammaincc.rs +137 -0
  91. rustylab-0.3.0/src/special/gammainccinv.rs +305 -0
  92. rustylab-0.3.0/src/special/gammaincinv.rs +297 -0
  93. rustylab-0.3.0/src/special/lbeta.rs +155 -0
  94. rustylab-0.3.0/src/special/lfactorial.rs +153 -0
  95. rustylab-0.3.0/src/special/lgamma.rs +205 -0
  96. rustylab-0.3.0/src/special/mod.rs +47 -0
  97. rustylab-0.3.0/src/stats/bernoulli.rs +1021 -0
  98. rustylab-0.3.0/src/stats/beta.rs +1362 -0
  99. rustylab-0.3.0/src/stats/binom.rs +1461 -0
  100. rustylab-0.3.0/src/stats/cauchy.rs +1132 -0
  101. rustylab-0.3.0/src/stats/chi2.rs +1155 -0
  102. rustylab-0.3.0/src/stats/expon.rs +1059 -0
  103. rustylab-0.3.0/src/stats/f.rs +1449 -0
  104. rustylab-0.3.0/src/stats/gamma.rs +1138 -0
  105. rustylab-0.3.0/src/stats/mod.rs +123 -0
  106. rustylab-0.3.0/src/stats/norm.rs +1086 -0
  107. rustylab-0.3.0/src/stats/poisson.rs +1294 -0
  108. rustylab-0.3.0/src/stats/randint.rs +1068 -0
  109. rustylab-0.3.0/src/stats/t.rs +1418 -0
  110. rustylab-0.3.0/src/stats/uniform.rs +1067 -0
  111. rustylab-0.3.0/src/utils.rs +3021 -0
  112. rustylab-0.3.0/tests/datasets/datasets.py +23 -0
  113. rustylab-0.3.0/tests/datasets/housing.parquet +0 -0
  114. rustylab-0.3.0/tests/datasets/iris.parquet +0 -0
  115. rustylab-0.3.0/tests/datasets/titanic.parquet +0 -0
  116. rustylab-0.3.0/tests/requirements-testing.txt +11 -0
  117. rustylab-0.3.0/tests/test_misc.py +268 -0
  118. rustylab-0.3.0/tests/test_optimize.py +234 -0
  119. rustylab-0.3.0/tests/test_plugin_symbols.py +25 -0
  120. rustylab-0.3.0/tests/test_special.py +1431 -0
  121. rustylab-0.3.0/tests/test_stats_bernoulli.py +1626 -0
  122. rustylab-0.3.0/tests/test_stats_beta.py +1869 -0
  123. rustylab-0.3.0/tests/test_stats_binom.py +1368 -0
  124. rustylab-0.3.0/tests/test_stats_cauchy.py +1791 -0
  125. rustylab-0.3.0/tests/test_stats_chi2.py +1962 -0
  126. rustylab-0.3.0/tests/test_stats_expon.py +1874 -0
  127. rustylab-0.3.0/tests/test_stats_f.py +1927 -0
  128. rustylab-0.3.0/tests/test_stats_gamma.py +1979 -0
  129. rustylab-0.3.0/tests/test_stats_norm.py +1844 -0
  130. rustylab-0.3.0/tests/test_stats_poisson.py +1460 -0
  131. rustylab-0.3.0/tests/test_stats_randint.py +1793 -0
  132. rustylab-0.3.0/tests/test_stats_t.py +2094 -0
  133. rustylab-0.3.0/tests/test_stats_uniform.py +938 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "model": "claude-opus-5",
3
+ "fallbackModel": ["claude-sonnet-5"],
4
+ "permissions": {
5
+ "defaultMode": "plan",
6
+ "deny": ["Edit", "Write", "NotebookEdit"]
7
+ }
8
+ }
@@ -0,0 +1,46 @@
1
+ name: Numerical Accuracy Testing
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+ types: [closed]
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: codeberg-medium-lazy
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Install Rust manually
20
+ run: |
21
+ curl https://sh.rustup.rs -sSf | sh -s -- -y
22
+ source $HOME/.cargo/env
23
+ rustup default stable
24
+
25
+ - name: Add Rust to PATH
26
+ run: echo "PATH=$HOME/.cargo/bin:$PATH" >> $GITHUB_ENV
27
+
28
+ - name: Create Python virtual environment
29
+ run: python3 -m venv venv
30
+
31
+ - name: Activate virtual environment and install Python dependencies
32
+ run: |
33
+ source venv/bin/activate
34
+ python -m pip install --upgrade pip
35
+ python -m pip install maturin
36
+ python -m pip install -r ./tests/requirements-testing.txt
37
+
38
+ - name: Build and install Rust extension
39
+ run: |
40
+ source venv/bin/activate
41
+ maturin develop --release --strip
42
+
43
+ - name: Run tests
44
+ run: |
45
+ source venv/bin/activate
46
+ pytest
@@ -0,0 +1,122 @@
1
+ name: Build wheels
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ build-wheels:
12
+ name: Build wheels on ${{ matrix.os }}
13
+ runs-on: ${{ matrix.os }}
14
+
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, windows-latest, macos-latest]
19
+
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Set up Python (for maturin & metadata)
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ - name: Install Rust
30
+ uses: dtolnay/rust-toolchain@stable
31
+
32
+ - name: Build wheels with maturin
33
+ uses: PyO3/maturin-action@v1
34
+ with:
35
+ command: build
36
+ args: >
37
+ --release
38
+ --strip
39
+ manylinux: "2014" # only used on Linux
40
+
41
+ - name: Verify the wheel ships the Python layer
42
+ shell: bash
43
+ run: |
44
+ python - <<'PY'
45
+ import pathlib, sys, zipfile
46
+
47
+ want = {p.relative_to("python").as_posix()
48
+ for p in pathlib.Path("python/rustylab").rglob("*.py")}
49
+ bad = False
50
+ for whl in sorted(pathlib.Path("target/wheels").glob("*.whl")):
51
+ have = {n for n in zipfile.ZipFile(whl).namelist() if n.endswith(".py")}
52
+ missing = sorted(want - have)
53
+ print(whl.name, "OK" if not missing else f"MISSING {missing}")
54
+ bad |= bool(missing)
55
+ sys.exit(1 if bad else 0)
56
+ PY
57
+
58
+ - name: Smoke-test the wheel
59
+ shell: bash
60
+ run: |
61
+ python -m venv smoke
62
+ PY=smoke/bin/python
63
+ [ -x smoke/Scripts/python.exe ] && PY=smoke/Scripts/python.exe
64
+ "$PY" -m pip install --upgrade pip
65
+ "$PY" -m pip install target/wheels/*.whl
66
+ "$PY" - <<'PY'
67
+ import polars as pl
68
+ from rustylab import expr, stats
69
+
70
+ print("eager:", stats.norm.pdf(0.0))
71
+ lf = pl.LazyFrame({"x": [0.0, 1.0, 2.0]}).select(p=expr.stats.norm.pdf("x"))
72
+ a = lf.collect(engine="in-memory")["p"].to_list()
73
+ b = lf.collect(engine="streaming")["p"].to_list()
74
+ assert a == b, (a, b)
75
+ print("plugin:", a)
76
+ PY
77
+
78
+ - name: Upload wheels
79
+ uses: actions/upload-artifact@v4
80
+ with:
81
+ name: wheels-${{ matrix.os }}
82
+ path: target/wheels/*.whl
83
+
84
+ build-sdist:
85
+ name: Build sdist
86
+ runs-on: ubuntu-latest
87
+
88
+ steps:
89
+ - name: Checkout
90
+ uses: actions/checkout@v4
91
+
92
+ - name: Set up Python (for maturin & metadata)
93
+ uses: actions/setup-python@v5
94
+ with:
95
+ python-version: "3.12"
96
+
97
+ - name: Build sdist with maturin
98
+ uses: PyO3/maturin-action@v1
99
+ with:
100
+ command: sdist
101
+ args: --out target/wheels
102
+
103
+ - name: Verify the sdist carries both halves of the package
104
+ shell: bash
105
+ run: |
106
+ python - <<'PY'
107
+ import pathlib, tarfile
108
+
109
+ sdist = next(pathlib.Path("target/wheels").glob("*.tar.gz"))
110
+ names = tarfile.open(sdist).getnames()
111
+ for required in ("Cargo.toml", "pyproject.toml", "src/lib.rs",
112
+ "python/rustylab/__init__.py",
113
+ "python/rustylab/expr/stats/norm.py"):
114
+ assert any(n.endswith("/" + required) for n in names), required
115
+ print(sdist.name, "OK -", len(names), "entries")
116
+ PY
117
+
118
+ - name: Upload sdist
119
+ uses: actions/upload-artifact@v4
120
+ with:
121
+ name: sdist
122
+ path: target/wheels/*.tar.gz
@@ -0,0 +1,76 @@
1
+ /target
2
+
3
+ # Quarto
4
+ **/.quarto/
5
+ **/*.quarto_ipynb
6
+
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ .pytest_cache/
10
+ *.py[cod]
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ .venv/
18
+ env/
19
+ bin/
20
+ build/
21
+ develop-eggs/
22
+ dist/
23
+ eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ include/
30
+ man/
31
+ venv/
32
+ *.egg-info/
33
+ .installed.cfg
34
+ *.egg
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+ pip-selfcheck.json
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .coverage
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+
49
+ # Translations
50
+ *.mo
51
+
52
+ # Mr Developer
53
+ .mr.developer.cfg
54
+ .project
55
+ .pydevproject
56
+
57
+ # Rope
58
+ .ropeproject
59
+
60
+ # Django stuff:
61
+ *.log
62
+ *.pot
63
+
64
+ .DS_Store
65
+
66
+ # Sphinx documentation
67
+ docs/_build/
68
+
69
+ # PyCharm
70
+ .idea/
71
+
72
+ # VSCode
73
+ #.vscode/
74
+
75
+ # Pyenv
76
+ .python-version
@@ -0,0 +1,6 @@
1
+ RustyLab
2
+ statsmodels
3
+ maturin
4
+ uv
5
+ Codeberg
6
+ Soper
@@ -0,0 +1,7 @@
1
+ {"rule":"UPPERCASE_SENTENCE_START","sentence":"^\\Qlinear_models\\E$"}
2
+ {"rule":"LICENCE_LICENSE_NOUN_SINGULAR","sentence":"^\\QMIT License\\E$"}
3
+ {"rule":"MORFOLOGIK_RULE_EN_GB","sentence":"^\\QCopyright © 2026 esuriddick\\E$"}
4
+ {"rule":"MORFOLOGIK_RULE_EN_GB","sentence":"^\\QTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\\E$"}
5
+ {"rule":"UPPERCASE_SENTENCE_START","sentence":"^\\Qspecial\\E$"}
6
+ {"rule":"LICENCE_LICENSE_NOUN_SINGULAR","sentence":"^\\QLicence\n{{< include ../LICENSE >}}\\E$"}
7
+ {"rule":"UPPERCASE_SENTENCE_START","sentence":"^\\Qmisc\nspecial\nstats\\E$"}
@@ -0,0 +1,22 @@
1
+ # rustylab
2
+
3
+ A Python library with a Rust backend, built to merge and unify functionality
4
+ from SciPy, NumPy, statsmodels, scikit-learn, and river into a single API.
5
+
6
+ ## Architecture
7
+
8
+ - Rust project where all functions are explicitly built for Python
9
+ interoperability (via `pyo3` or similar crates).
10
+ - The project includes custom Rust modules that are part of this codebase,
11
+ not external crates.
12
+ - Internal Rust functions are called through: `use crate::constants`,
13
+ `use crate::optimize`, `use crate::special`, `use crate::stats`,
14
+ `use crate::utils`, etc.
15
+
16
+ ## How to work with me
17
+
18
+ - Do not edit files. Propose changes as suggestions or copy-pasteable
19
+ code replacements, and show enough surrounding context that I can
20
+ place the change myself.
21
+ - When suggesting a function, respect the existing `crate::` module
22
+ boundaries above rather than introducing new top-level modules.
@@ -0,0 +1,122 @@
1
+ # Contributing to RustyLab
2
+ Thank you for your interest in contributing to RustyLab! Contributions of all kinds are welcome — whether you want to report an issue, propose a feature, improve documentation, or submit code.
3
+
4
+ ## Getting Help
5
+ Something is not working as expected? Is there a feature you would like to see implemented but are unsure how to approach it? Please head to the [Issues Page](https://codeberg.org/esuriddick/rustylab/issues) and open an issue.
6
+
7
+ When creating an issue, please include one of the following tags in the title:
8
+
9
+ - **FEATURE** — propose a new feature or an improvement to an existing one.
10
+ - **BUG** — report unexpected or incorrect behaviour.
11
+ - **HELP** — ask a question that does not fit the categories above.
12
+
13
+ Examples:
14
+
15
+ ```bash
16
+ [FEATURE] Generalized Linear Models
17
+ [BUG] Function special.factorial is not returning the correct value
18
+ [HELP] How to install RustyLab in Python?
19
+ ```
20
+
21
+ ## Propose your own code
22
+
23
+ ### Prerequisites
24
+
25
+ * **Python:** 3.12 or higher for development; released wheels support 3.10+.
26
+ * **Rust:** 1.92.0 or higher
27
+
28
+ ### Optional Tools
29
+
30
+ * **Python package manager:** [uv](https://docs.astral.sh/uv/)
31
+ * **IDE:** [Positron](https://positron.posit.co/)
32
+
33
+ ### Project layout
34
+
35
+ RustyLab is a hybrid package - a Rust core plus a Python layer, both shipped in the wheel:
36
+
37
+ * `src/**` - the Rust core. Eager functions are exposed with `pyo3`; expression functions
38
+ are exposed with `#[polars_expr]`, which turns each one into an exported symbol.
39
+ * `python/rustylab/**` - the Python layer. The `expr/**` modules are thin wrappers that call
40
+ `register_plugin_function(..., function_name="…")`, where the name **must** match the
41
+ symbol exported by the Rust side.
42
+ * `tests/**` and `benchmarks/**` - the numerical accuracy suite and the benchmark notebooks.
43
+ * `docs/**` - the Quarto sources rendered into the Wiki.
44
+
45
+ ### Quick setup
46
+ 1. **Clone the repository**:
47
+ ```bash
48
+ git clone https://codeberg.org/esuriddick/rustylab.git
49
+ cd rustylab
50
+ ```
51
+
52
+ 2. **Set up the required virtual environments**
53
+
54
+ There are three virtual environments, namely:
55
+
56
+ * **Development** (`./requirements.txt`) — includes `maturin` for building and packaging Rust-based Python extension.
57
+ * **Benchmarking** (`./benchmarks/requirements-benchmarking.txt`) — includes packages required for benchmarking.
58
+ * **Testing** (`./tests/requirements-testing.txt`) — includes packages used for numerical accuracy tests.
59
+
60
+ If you use `uv`, you can create the Development environment with:
61
+ ```bash
62
+ uv venv
63
+ uv pip install -r requirements.txt
64
+ ```
65
+
66
+ To set up the Benchmarking environment:
67
+ ```bash
68
+ cd benchmarks
69
+ uv venv
70
+ uv pip install -r requirements-benchmarking.txt
71
+ ```
72
+
73
+ To set up the Testing environment:
74
+ ```bash
75
+ cd ../ # To return to the repository's root folder
76
+ cd tests
77
+ uv venv
78
+ uv pip install -r requirements-testing.txt
79
+ ```
80
+
81
+ ### Development Workflow
82
+ Please follow the existing project structure and create new modules only when necessary. RustyLab aims to maintain conceptual alignment with established Python libraries such as SciPy, scikit-learn, and statsmodels.
83
+
84
+ When adding new functions or classes:
85
+
86
+ - Provide clear documentation, so others can understand and review your work.
87
+ - Ensure your implementation is testable.
88
+ - Include benchmarks and tests where appropriate.
89
+
90
+ An expression function is not finished until all five exist:
91
+
92
+ 1. The Rust implementation, exposed with `#[polars_expr]` in the right `crate::` module;
93
+ 2. The Python wrapper in `python/rustylab/expr/**`, whose `function_name` matches the exported symbol - `tests/test_plugin_symbols.py` enforces this;
94
+ 3. A test that collects the expression under **both** the `in-memory` and `streaming` engines and compares the results;
95
+ 4. A docstring on both sides - the eager one in `src/**`, the expression one in the Python wrapper;
96
+ 5. The matching section in `docs/**`.
97
+
98
+ To debug your work before submission, build the RustyLab Python package using the Development environment:
99
+ ```bash
100
+ source .venv/bin/activate # Linux / macOS
101
+ .\.venv\Scripts\activate # Windows
102
+ maturin develop --release --strip
103
+ ```
104
+
105
+ `maturin develop` installs **both** halves of the package, and it *copies* the modules under `python/rustylab/**` into `site-packages`. A Python-only edit is therefore not live: rebuild after touching `python/rustylab/**`, exactly as you would after touching `src/**`.
106
+
107
+ This allows you to import and test RustyLab directly from your local environment.
108
+
109
+ ### Commit Guidelines
110
+ Commits should follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. Examples:
111
+ ```
112
+ docs: documentation only changes
113
+ feat: a new feature is introduced
114
+ fix: a bug is fixed
115
+ perf: code change that improves performance
116
+ refactor: code change that neither fixes a bug nor adds a feature
117
+ style: changes that do not affect the meaning of the code (whitespace, formatting, missing semi-colons, etc.)
118
+ test: add missing tests or correcting existing tests
119
+ ```
120
+
121
+ ### Licence of contributions
122
+ By contributing, you agree that your contributions will be licensed under the [MIT licence](https://opensource.org/license/MIT).