Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
31fd3d2
Add initial version of .pre-commit-config.yaml
eriknw Jul 23, 2025
4eb2bc1
validate-pyproject
eriknw Jul 23, 2025
92311fe
autoflake
eriknw Jul 23, 2025
c3d3b3f
pyupgrade
eriknw Jul 23, 2025
ae57dc8
black (line-length = 100)
eriknw Jul 23, 2025
22d2a42
ruff
eriknw Jul 23, 2025
2394cd1
ruff extend-select (no changes)
eriknw Jul 23, 2025
0dcc8cf
isort via ruff
eriknw Jul 23, 2025
d7b2a4f
flake8-return via ruff
eriknw Jul 23, 2025
d885624
Enable ruff linting, and some fixes
eriknw Jul 23, 2025
3f6acab
pyroma
eriknw Jul 23, 2025
5441abe
SIM102 (manually)
eriknw Jul 23, 2025
7dc1d04
codespell
eriknw Jul 23, 2025
b3846dc
taplo to auto-format pyproject.toml
eriknw Jul 23, 2025
55529cc
yamllint
eriknw Jul 23, 2025
00806b5
prettier
eriknw Jul 23, 2025
3b454a6
sphinx-lint
eriknw Jul 23, 2025
6f79685
check-jsonschema and dependabot.yml
eriknw Jul 23, 2025
fb6c5d9
zizmor (TODO)
eriknw Jul 23, 2025
0e91f36
blacken-docs
eriknw Jul 23, 2025
d8fa189
pygrep-hooks and updates from sp-repo-review
eriknw Jul 23, 2025
750f566
Add some local pre-commit hooks
eriknw Jul 23, 2025
f408447
Add pre-commit github action
eriknw Jul 23, 2025
cf3e06f
Clean up based on actual scientific-python cookie cutter template
eriknw Jul 23, 2025
5af01bd
Fix docs (oops!)
eriknw Jul 23, 2025
c6a21fb
Use `importlib_metadata.version` instead of `importlib.metadata.versi…
eriknw Jul 23, 2025
a095941
Add `pre-commit` to `dev` dependency group
eriknw Jul 23, 2025
3bbab2e
auto-walrus (hehehe)
eriknw Jul 24, 2025
b79a8fa
Use `Path(filepath).read_text()`
eriknw Jul 24, 2025
cdb1ee8
Use `Path(filepath).write_text(text)`
eriknw Jul 24, 2025
e20f260
oops fix whitespace
eriknw Jul 24, 2025
5f56a61
Iterate over `graph` since not mutating
eriknw Jul 24, 2025
b0ae082
bump ruff
eriknw Jul 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
actions:
patterns:
- "*"
7 changes: 4 additions & 3 deletions .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
workflow_dispatch:
inputs:
deploy:
description: 'Publish docs?'
description: "Publish docs?"
required: true
type: boolean

Expand All @@ -32,6 +32,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- uses: actions/setup-python@v5
with:
Expand All @@ -58,8 +59,8 @@ jobs:
if: inputs.deploy

permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source

# Deploy to the github-pages environment
environment:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- uses: actions/setup-python@v5
with:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: pre-commit checks

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
name: pre-commit-hooks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: pre-commit/action@v3.0.1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,4 @@ Thumbs.db
# Common editor files
*~
*.swp
*.swo
163 changes: 163 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# https://pre-commit.com/
#
# Before first use:
#
# $ pre-commit install
#
ci:
autofix_prs: false
autoupdate_schedule: quarterly
autoupdate_commit_msg: "chore: update pre-commit hooks"
autofix_commit_msg: "style: pre-commit fixes"
skip: [no-commit-to-branch]
fail_fast: false
default_language_version:
python: python3
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
# Sanity checks
- id: check-added-large-files
- id: check-case-conflict
# - id: check-executables-have-shebangs # No executable files yet
- id: check-illegal-windows-names
- id: check-merge-conflict
# Checks based on file type
- id: check-ast
# - id: check-json # No json files yet
- id: check-symlinks
- id: check-toml
# - id: check-xml # No xml files yet
- id: check-yaml
# Detect mistakes
- id: check-vcs-permalinks
- id: debug-statements
- id: destroyed-symlinks
- id: detect-private-key
- id: forbid-submodules
# Automatic fixes
- id: end-of-file-fixer
- id: mixed-line-ending
args: [--fix=lf]
# - id: requirements-txt-fixer # No requirements.txt file yet
- id: trailing-whitespace
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.24.1
hooks:
- id: validate-pyproject
name: Validate pyproject.toml
# Remove unnecessary imports (currently behaves better than ruff)
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.1
hooks:
- id: autoflake
args: [--in-place]
# Let's keep `pyupgrade` even though `ruff --fix` probably does most of it
- repo: https://github.com/asottile/pyupgrade
rev: v3.20.0
hooks:
- id: pyupgrade
args: [--py310-plus]
# black often looks better than ruff-format
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/adamchainz/blacken-docs
rev: 1.19.1
hooks:
- id: blacken-docs
additional_dependencies: [black==25.1.0]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.5
hooks:
- id: ruff-check
args: [--fix-only, --show-fixes]
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
types_or: [python, markdown, rst, toml, yaml]
additional_dependencies:
- tomli; python_version<'3.11'
- repo: https://github.com/MarcoGorelli/auto-walrus
rev: 0.3.4
hooks:
- id: auto-walrus
args: [--line-length, "100"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.5
hooks:
- id: ruff-check
# - id: ruff-format # Prefer black, but may temporarily uncomment this to see
# `pyroma` may help keep our package standards up to date if best practices change.
# This is probably a "low value" check though and safe to remove if we want faster pre-commit.
# - repo: https://github.com/regebro/pyroma
# rev: "5.0"
# hooks:
# - id: pyroma
# args: [-n, "9", .] # Need author email to score a 10
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.6.2
hooks:
- id: prettier
args: [--prose-wrap=preserve]
- repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v1.0.0
hooks:
- id: sphinx-lint
args: [--enable, all, "--disable=line-too-long,leaked-markup"]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
- id: rst-backticks
- id: rst-directive-colons
- id: rst-inline-touching-normal
- id: python-check-blanket-noqa
- id: python-check-blanket-type-ignore
- id: python-no-eval
- id: python-no-log-warn
- id: text-unicode-replacement-char
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: v0.9.3
hooks:
- id: taplo-format
- repo: https://github.com/adrienverge/yamllint
rev: v1.37.1
hooks:
- id: yamllint
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.33.2
hooks:
- id: check-dependabot
- id: check-github-workflows
# TODO: get zizmor to pass, and maybe set it up as a github action
# - repo: https://github.com/woodruffw/zizmor-pre-commit
# rev: v1.11.0
# hooks:
# - id: zizmor
- repo: local
hooks:
- id: disallow-caps
name: Disallow improper capitalization
language: pygrep
entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|RST|PyLint
exclude: (.pre-commit-config.yaml|docs/pages/guides/style\.md)$
- id: disallow-words
name: Disallow certain words
language: pygrep
entry: "[Ff]alsey"
exclude: .pre-commit-config.yaml$
- id: disallow-bad-permalinks
name: Disallow _ in permalinks
language: pygrep
entry: "^permalink:.*_.*"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: no-commit-to-branch # No commit directly to main
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
5 changes: 5 additions & 0 deletions .yamllint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
extends: default
rules:
document-start: disable
line-length: disable
40 changes: 22 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ based on feedback.**

Spatch is a dispatching tool with a focus on scientific python libraries.
It integrates two forms of dispatching into a single backend system:
* Type dispatching for the main type used by a library.

- Type dispatching for the main type used by a library.
In the scientific python world, this is often the array object.
* Backend selection to offer alternative implementations to users.
- Backend selection to offer alternative implementations to users.
These may be faster or less precise, but using them should typically
not change code behavior drastically.

Expand All @@ -26,7 +27,7 @@ to a large scale deployment.

Unfortunately, providing code for a host of such types isn't easy
and the original library authors usually have neither the bandwidth nor
expertise to do it. Additionally, such layers would have to be optional
expertise to do it. Additionally, such layers would have to be optional
components of the library.

`spatch` allows for a solution to this dilemma by allowing a third party
Expand All @@ -35,7 +36,7 @@ to enable library functions to work with alternative types.
It should be noted that spatch is not a generic multiple dispatching
library. It is opinionated about being strictly typed (we can and probably
will support subclasses in the future, though).
It also considers all arguments identically. I.e. if a function takes
It also considers all arguments identically. I.e. if a function takes
two inputs (of the kind we dispatch for), there is no distinction for
their order.
Besides these two things, `spatch` is however a typical type dispatching
Expand All @@ -56,11 +57,12 @@ For example, we may have a faster algorithm that is parallelized while the
old one was not. Or an implementation that dispatches to the GPU but still
returns NumPy arrays (as the library always did).

Backend selection _modifies_ behavior rather than extending it. In some
Backend selection _modifies_ behavior rather than extending it. In some
cases those modifications may be small (maybe it is really only faster).
For the user, backend _selection_ often means that they should explicitly
select a preferred backend (e.g. over the default implementation).
This could be for example via a context manager:

```python
with backend_opts(prioritize="gpu_backend"):
library.function() # now running on the GPU
Expand All @@ -76,36 +78,38 @@ with backend_opts(prioritize="gpu_backend"):
it should be considered a prototype when it comes to API stability.

Some examples for missing things we are still working on:
* No way to conveniently see which backends may be used when calling a

- No way to conveniently see which backends may be used when calling a
function (rather than actually calling it). And probably more inspection
utilities.
* We have implemented the ability for a backend to defer and not run,
- We have implemented the ability for a backend to defer and not run,
but not the ability to run anyway if there is no alternative.
* The main library implementation currently can't distinguish fallback
- The main library implementation currently can't distinguish fallback
and default path easily. It should be easy to do this (two functions,
`should_run`, or just via `uses_context`).
* `spatch` is very much designed to be fast but that doesn't mean it
is particularly fast yet. We may need to optimize parts (potentially
- `spatch` is very much designed to be fast but that doesn't mean it
is particularly fast yet. We may need to optimize parts (potentially
lowering parts to a compiled language).
* We have not implemented tools to test backends, e.g. against parts
of the original library. We expect that "spatch" actually includes most
tools to do this. For example, we could define a `convert` function
- We have not implemented tools to test backends, e.g. against parts
of the original library. We expect that "spatch" actually includes most
tools to do this. For example, we could define a `convert` function
that backends can implement to convert arguments in tests as needed.

There are also many smaller or bigger open questions and those include whether
the API proposed here is actually quite what we want.
Other things are for example whether we want API like:
* `dispatchable.invoke(type=, backend=)`.
* Maybe libraries should use `like=` in functions that take no dispatchable

- `dispatchable.invoke(type=, backend=)`.
- Maybe libraries should use `like=` in functions that take no dispatchable
arguments.
* How do we do classes such as scikit-learn estimators. A simple solution might
- How do we do classes such as scikit-learn estimators. A simple solution might
a `get_backend(...)` dispatching explicitly once. But we could use more involved
schemes, rather remembering the dispatching state of the `.fit()`.

We can also see many small conveniences, for example:
* Extract the dispatchable arguments from type annotations.
* Support a magic `Defer` return, rather than the `should_run` call.

- Extract the dispatchable arguments from type annotations.
- Support a magic `Defer` return, rather than the `should_run` call.

# Usage examples

Expand Down
1 change: 0 additions & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@ html: Makefile
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

4 changes: 2 additions & 2 deletions docs/source/api/for_backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Before writing a backend, you need to think about a few things:
by the user.

Please check the example linked above. These example entry-points include
code that means running them modifies them in-place if the `@implements`
code that means running them modifies them in-place if the ``@implements``
decorator is used (see next section).

Some of the most important things are:
Expand Down Expand Up @@ -94,7 +94,7 @@ The following fields are supported for each function:

- ``function``: The implementation to dispatch to.
- ``should_run`` (optional): A function that gets all inputs (and context)
and can decide to defer. Unless you know things will error, try to make sure
and can decide to defer. Unless you know things will error, try to make sure
that this function is light-weight.
- ``uses_context``: Whether the implementation needs a ``DispatchContext``.
- ``additional_docs`` (optional): Brief text to add to the documentation
Expand Down
1 change: 0 additions & 1 deletion docs/source/api/for_libraries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ API to create dispatchable functions
.. autoclass:: spatch.backend_system.BackendSystem
:class-doc-from: init
:members: dispatchable, backend_opts

8 changes: 4 additions & 4 deletions docs/source/api/for_users.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ Libraries will re-expose all of this functionality under their own API/names.
There are currently three global environment variables to modify dispatching
behavior at startup time:

* ``<SPECIFIC_NAME>_PRIORITIZE``: Comma seperated list of backends.
* ``<SPECIFIC_NAME>_PRIORITIZE``: Comma separated list of backends.
This is the same as :py:class:`BackendOpts` ``prioritize=`` option.
* ``<SPECIFIC_NAME>_BLOCK``: Comma seperated list of backends.
* ``<SPECIFIC_NAME>_BLOCK``: Comma separated list of backends.
This prevents loading the backends as if they were not installed.
No backend code will be executed (its entry-point will not be loaded).
* ``<SPECIFIC_NAME>_SET_ORDER``: Comma seperated list of backend orders.
seperated by ``>``. I.e. ``name1>name2,name3>name2`` means that ``name1``
* ``<SPECIFIC_NAME>_SET_ORDER``: Comma separated list of backend orders.
separated by ``>``. I.e. ``name1>name2,name3>name2`` means that ``name1``
and ``name3`` are ordered before ``name2``. This is more fine-grained
than the above two and the above two take precedence. Useful to fix relative
order of backends without affecting the priority of backends not listed
Expand Down
Loading