Contributing#
scikit-lr is developed on GitHub, where bugs and feature requests are tracked as issues and changes are submitted as pull requests. Communications on all channels should respect our code of conduct.
scikit-lr follows the conventions of scikit-learn, so this guide only covers what is specific to scikit-lr and links to the scikit-learn contributing guide for the rest.
Ways to contribute#
Code is not the only way to help the project. Reporting the problems that you find when using scikit-lr, improving the documentation, investigating the open issues and reviewing the pull requests of other contributors are just as valuable. It also helps to reference scikit-lr from your articles and websites, or to star it on GitHub to say that you use it.
Licensing your contribution#
Pull requests are accepted under the MIT license of the project, so you must have the right to license every part of your contribution under it. Code taken from other projects must have a compatible license and keep its notice.
Automated contributions policy#
Contributing to scikit-lr requires human judgment and an understanding of the project, so issues and pull requests generated by fully automated tools are closed without review.
You may use AI tools to help you, but you must review, understand and test every change that they make before submitting it under your name, and be able to explain it when asked. State in the description of the pull request what you used them for, as its template asks. Do not paste text generated by AI tools in issues, pull requests or comments, beyond fixing the grammar or translating your own words.
This policy does not cover the automated tools that the maintainers set up for the project, such as the bot that updates the pinned versions of the tools and the actions, or the workflows that open an issue when they fail. Their pull requests are reviewed by a maintainer before merging, like any other.
Submitting a bug report or a feature request#
Before opening an issue, check that it is not already covered by another issue or pull request. A feature request should explain the problem that it solves and, for a new estimator or metric, cite the article that describes it.
A good bug report contains a minimal reproducer:
a short snippet that anyone can run to see the problem, with synthetic data instead
of your own. Include the full traceback of any exception, and the versions of
scikit-lr, of its dependencies and of the platform, which sklr.show_versions()
prints:
$ python -c "import sklr; sklr.show_versions()"
If sklr cannot be imported, report the error, and the output of
sklearn.show_versions() instead.
Contributing code and documentation#
The changes are submitted as pull requests from a fork of the repository. To avoid duplicated work, search the issues and pull requests first and, for anything beyond a small fix, open an issue to discuss it before writing the code.
Development workflow#
Set up the development environment, from a clone of your fork with the main repository as the
upstreamremote.Create a branch for your changes from the latest
master:$ git fetch upstream $ git switch -c my-feature upstream/master
Commit your changes. The
pre-commithooks run on each commit and may fix some files themselves; in that case, add them again and repeat the commit:$ git add <files> $ git commit
Push the branch to your fork and open a pull request against
master:$ git push -u origin my-feature
To bring in the changes made to master meanwhile, merge upstream/master into
your branch and solve the conflicts, if any.
Pull request checklist#
Before asking for a review, make sure that:
The pull request is focused on one change and does not modify unrelated lines.
The title follows Conventional Commits (
feat:,fix:,docs:,test:, …) and summarizes the change. The description explains why it is needed and links the issue that it solves with a keyword such asFixes #<issue>, so that the issue is closed with the merge.The code follows the coding guidelines.
New code is tested, and a bug fix comes with a test that fails without it (see Testing and improving test coverage).
The public API is documented, and new features are described in the user guide and shown in an example (see Documentation).
A change that affects users has a changelog entry.
The checks pass locally, and the CI passes once the pull request is opened.
An incomplete contribution can be opened as a draft pull request, and marked as ready for review when it is finished.
Continuous integration#
The pull requests are checked by these workflows of GitHub Actions:
“Unit tests” builds scikit-lr and runs its tests on Linux, macOS and Windows, with every supported Python version and with the minimum and latest versions of the dependencies.
“Code quality checks” runs the
pre-commithooks on the whole repository.“Wheels and source distribution” builds and tests the wheels of every platform and the source distribution, as they are released.
“Coverage” measures the lines that the tests cover and reports the changes to Codecov.
“Documentation” builds the documentation, which is available as an artifact of the run.
“CodeQL” looks for security problems in the Python code and the workflows.
“Check changelog” checks that a pull request that changes the tests adds a changelog entry named after its number, unless a maintainer labels it “no changelog needed” because it does not affect users.
The workflow “Upcoming scikit-learn” runs every night instead, and builds and tests scikit-lr against the next release of scikit-learn (see Coupling with the version of scikit-learn). When it fails, it opens an issue, which it closes once it passes again.
Checks#
Before submitting a pull request, make sure that the tests and the linters pass:
$ pytest
$ pre-commit run --all-files
The pre-commit hooks run ruff (lint and format), pyrefly (type checking),
cython-lint, sphinx-lint (reStructuredText) and codespell, among others.
They only look at the files tracked by git, so add the new files before running
them.
Testing and improving test coverage#
The tests use pytest and live in the tests directory of each subpackage. The
whole suite also runs the examples of the docstrings and of the documentation pages,
but you can run only the tests related to your changes:
$ pytest sklr/<subpackage>/tests/test_<module>.py
$ pytest sklr/<subpackage>/<module>.py
$ pytest doc/<page>.rst
The scikit-learn pytest tips apply here too. Every warning is turned into an error, and a test marked as an expected failure fails if it passes.
The tests of scikit-lr follow these rules:
Tests import the public API from its public module, as users do.
Every estimator passes the scikit-learn estimator checks with the
parametrize_with_checksofsklr/utils/_estimator_checks.py. It runs the checks ofparametrize_with_checks(), whose targets are meant for classifiers and regressors, with the targets of the rankers turned into rankings. A check that cannot pass is listed inexpected_failed_checkswith the reason.Numerical results are checked against independent oracles: a naive implementation, a known property, or an equivalent function of NumPy, SciPy or scikit-learn, never against the output of a previous version.
The tests also cover the edge cases: empty and single-sample inputs, ties, different dtypes,
n_jobsand reproducibility withrandom_state.Random seeds are fixed, and tolerances are explicit, with
numpy.testing.assert_allclose(), and justified in a comment.
New code should be covered by the tests. To see the lines that they miss:
$ coverage run -m pytest
$ coverage report --show-missing
The coverage of the Cython extensions is measured every night by the continuous
integration, which compiles them with the linetrace option of meson.options.
Documentation#
The documentation has four parts, written in reStructuredText:
The docstrings of the public API, in the code of
sklr, from which the API reference is generated.The user guide, which explains the problems and the algorithms with the details that the API reference leaves out, in
doc.The examples, in
examples, which Sphinx-Gallery runs and renders when the documentation is built.The release history, with a file per minor release in
doc/whats_new, which is generated from the changelog entries of the pull requests.
Docstrings follow the numpydoc style guide and, for what it leaves
open, the guidelines for docstrings, the user guide and the references of
scikit-learn. Lines are at most 88
characters long, in docstrings and in the .rst pages. The docstrings of the
public API pass the validation checks of numpydoc,
except the ones that scikit-learn ignores, as sklr/tests/test_docstrings.py
tests. The examples of the docstrings and of the pages run as tests without the
names of the module where they are, so they must import everything they use.
Changelog entries#
A pull request that affects users describes its changes in a changelog fragment, a
short reStructuredText file in doc/whats_new/upcoming_changes that towncrier adds to the release history of the upcoming
version when it is released. The release history is not edited by hand, so that the
pull requests do not conflict with each other.
The fragment goes in the directory of the public module that the pull request
changes, such as sklr, or in custom-top-level if it changes none in
particular. Each directory is a section of [tool.towncrier] in pyproject.toml,
so a pull request that adds a public module also adds its section there. The
fragment is named <pull request>.<type>.rst, with the number of the pull
request and one of these types, whose meaning the legend of the release history
explains:
major-featurefeatureefficiencyenhancementfixapiother, without a badge, mostly forcustom-top-level.
A pull request with more than one change of the same type and directory adds a
counter to the name of the other fragments, as in <pull request>.<type>.1.rst.
Each fragment is a single bullet point, which ends by crediting its authors with the
:user: role and their GitHub username, and the link to the pull request is added
when the release history is generated. For example, the pull request that added
show_versions() added 99.feature.rst to the sklr directory:
* :func:`show_versions` prints the versions of scikit-lr, of the scikit-learn that
it was built against and of the dependencies, to include them in bug reports.
By :user:`Juan Carlos Alfaro Jiménez <alfaro96>`.
The documentation of the development version renders the fragments into the release history of the upcoming version. To preview it locally, run:
$ towncrier build --draft
Building the documentation#
The documentation is built with Sphinx, from the doc directory:
$ make -C doc html
The pages are written to doc/_build/html. Every warning is an error, so that the
pages that would render wrongly are not published. The build needs network access,
to download the inventories that link to the documentation of Python, NumPy, SciPy
and scikit-learn. make -C doc clean removes the build and the pages generated
from the docstrings and the examples.
Coding guidelines#
scikit-lr follows the coding guidelines of scikit-learn, and its estimators follow the scikit-learn API. In addition:
Only the public API is exported; every other module, function and attribute starts with an underscore. Changes to the public API are recorded in the release history.
The signatures of the public API have type hints, which are distributed with the package. The private code does not, as in scikit-learn.
The private API of scikit-learn is only imported by
sklr/utils/_sklearn_compat.py, which adapts it to the supported versions, and apre-commithook enforces it. The internals of scikit-learn used from Cython follow their own rules.Error messages include the value that was received, and warnings use a specific category.
Comments explain why the code does what it does, not what it does.
New dependencies need to be discussed in an issue first.
Code review#
Anybody can review a pull request, and doing so is a good way to learn the code base. The scikit-learn code review guidelines describe what to look at, and how to communicate it.