[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "array-api-extra"
authors = [
  { name = "Lucas Colley", email = "lucas.colley8@gmail.com" },
  # { name = "Open Source Contributors" },  # https://github.com/pypi/warehouse/issues/14813
]
description = "Extra array functions built on top of the array API standard."
readme = "README.md"
license.file = "LICENSE"
requires-python = ">=3.10"
classifiers = [
  "Intended Audience :: Developers",
  "License :: OSI Approved :: MIT License",
  "Operating System :: OS Independent",
  "Programming Language :: Python",
  "Programming Language :: Python :: 3",
  "Programming Language :: Python :: 3 :: Only",
  "Programming Language :: Python :: 3.10",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3.12",
  "Programming Language :: Python :: 3.13",
  "Typing :: Typed",
]
dynamic = ["version"]
dependencies = ["array-api-compat>=1.12.0,<2"]

[project.urls]
Homepage = "https://github.com/data-apis/array-api-extra"
"Bug Tracker" = "https://github.com/data-apis/array-api-extra/issues"
Changelog = "https://github.com/data-apis/array-api-extra/releases"


# Hatch

[tool.hatch]
version.path = "src/array_api_extra/__init__.py"


# Pixi

[tool.pixi.project]
channels = ["https://prefix.dev/conda-forge"]
platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"]

[tool.pixi.dependencies]
python = ">=3.10,<3.14"
array-api-compat = ">=1.12.0,<2"

[tool.pixi.pypi-dependencies]
array-api-extra = { path = ".", editable = true }

[tool.pixi.feature.lint.dependencies]
typing-extensions = ">=4.13.2"
pre-commit = ">=4.2.0"
pylint = ">=3.3.6"
basedmypy = ">=2.10.0"
basedpyright = ">=1.29.1"
numpydoc = ">=1.8.0,<2"
# import dependencies for mypy:
array-api-strict = ">=2.3.1"
numpy = ">=2.1.3"
pytest = ">=8.3.5"
hypothesis = ">=6.131.8"
dask-core = ">=2025.5.0"  # No distributed, tornado, etc.
# NOTE: don't add cupy, jax, pytorch, or sparse here,
# as they slow down mypy and are not portable across target OSs

[tool.pixi.feature.lint.tasks]
pre-commit-install = { cmd = "pre-commit install", description = "Install pre-commit"}
pre-commit = { cmd = "pre-commit run --all-files", description = "Run pre-commit"}
mypy = { cmd = "mypy", description="Type check with mypy"}
pylint = { cmd = "pylint array_api_extra", cwd = "src" , description = "Lint using pylint"}
pyright = { cmd = "basedpyright", description = "Type check with basedpyright"}
lint = { depends-on = ["pre-commit", "pylint", "mypy", "pyright"] , description = "Run pre-commit, pylint, mypy, and pyright"}

[tool.pixi.feature.tests.dependencies]
pytest = ">=8.3.5"
pytest-cov = ">=6.1.1"
hypothesis = ">=6.131.8"
array-api-strict = ">=2.3.1"
numpy = ">=1.22.0"

[tool.pixi.feature.tests.tasks]
tests = { cmd = "pytest -v", description = "Run tests"}
tests-cov = { cmd="pytest -v -ra --cov --cov-report=xml --cov-report=term --durations=20", description = "Run tests with coverage"}

clean-vendor-compat = { cmd = "rm -rf vendor_tests/array_api_compat", description = "Delete the existing vendored version of array-api-compat"}
clean-vendor-extra = { cmd = "rm -rf vendor_tests/array_api_extra" , description = "Delete the existing vendored version of array-api-extra"}
copy-vendor-compat = { cmd = "cp -r $(python -c 'import site; print(site.getsitepackages()[0])')/array_api_compat vendor_tests/", depends-on = ["clean-vendor-compat"] , description = "Vendor a clean copy of array-api-compat"}
copy-vendor-extra = { cmd = "cp -r src/array_api_extra vendor_tests/", depends-on = ["clean-vendor-extra"] , description = "Vendor a clean copy of array-api-extra"}
tests-vendor = { cmd = "pytest -v vendor_tests", depends-on = ["copy-vendor-compat", "copy-vendor-extra"] , description = "Check that array-api-extra and array-api-compat can be vendored together" }

tests-ci = { depends-on = ["tests-cov", "tests-vendor"] , description = "Run tests with coverage and vendor tests"}
coverage = { cmd = "coverage html", depends-on = ["tests-cov"], description = "Generate test coverage html report"}
open-coverage = { cmd = "open htmlcov/index.html", depends-on = ["coverage"] , description = "Open test coverage report"}

[tool.pixi.feature.docs.dependencies]
sphinx = ">=7.4.7"
furo = ">=2024.8.6"
myst-parser = ">=4.0.1"
sphinx-copybutton = ">=0.5.2"
sphinx-autodoc-typehints = ">=1.25.3"
# Needed to import parsed modules with autodoc
dask-core = ">=2025.5.0"  # No distributed, tornado, etc.
pytest = ">=8.3.5"
typing-extensions = ">=4.13.2"
numpy = ">=2.1.3"

[tool.pixi.feature.docs.tasks]
docs = { cmd = "sphinx-build -E -W . build/", cwd = "docs" , description = "Build docs"}
open-docs = { cmd = "open build/index.html", cwd = "docs", depends-on = ["docs"] , description = "Open the generated docs"}

[tool.pixi.feature.dev.dependencies]
ipython = ">=7.33.0"

[tool.pixi.feature.dev.tasks]
ipython = { cmd = "ipython" , description = "Launch ipython"}

[tool.pixi.feature.py310.dependencies]
python = "~=3.10.0"

[tool.pixi.feature.py313.dependencies]
python = "~=3.13.0"

[tool.pixi.feature.numpy1.dependencies]
# Oldest NumPy version supported by scikit-learn.
# Note that this is older than what SPEC0 recommends.
numpy = "=1.22.0"

# Backends that can run on CPU-only hosts
# Note: JAX and PyTorch will install CPU variants.
[tool.pixi.feature.backends.dependencies]
pytorch = ">=2.6.0"
dask-core = ">=2025.5.0"  # No distributed, tornado, etc.
sparse = ">=0.16.0"

[tool.pixi.feature.backends.target.linux-64.dependencies]
jax = ">=0.5.2"

[tool.pixi.feature.backends.target.osx-64.dependencies]
jax = ">=0.5.2"

[tool.pixi.feature.backends.target.osx-arm64.dependencies]
jax = ">=0.5.2"

[tool.pixi.feature.backends.target.win-64.dependencies]
# jax = "*"  # unavailable

# Backends that require a GPU host and a CUDA driver.
# Note that JAX and PyTorch automatically prefer CUDA variants
# thanks to the `system-requirements` below, *if available*.
# We request them explicitly below to ensure that we don't
# quietly revert to CPU-only in the future, e.g. when CUDA 13
# is released and CUDA 12 builds are dropped upstream.
[tool.pixi.feature.cuda-backends]
system-requirements = { cuda = "12" }

[tool.pixi.feature.cuda-backends.target.linux-64.dependencies]
cupy = ">=13.4.1"
jaxlib = { version = ">=0.5.2", build = "cuda12*" }
pytorch = { version = ">=2.6.0", build = "cuda12*" }

[tool.pixi.feature.cuda-backends.target.osx-64.dependencies]
# cupy = "*"  # unavailable
# jaxlib = { version = "*", build = "cuda12*" }  # unavailable
# pytorch = { version = "*", build = "cuda12*" }  # unavailable

[tool.pixi.feature.cuda-backends.target.osx-arm64.dependencies]
# cupy = "*"  # unavailable
# jaxlib = { version = "*", build = "cuda12*" }  # unavailable
# pytorch = { version = "*", build = "cuda12*" }  # unavailable

[tool.pixi.feature.cuda-backends.target.win-64.dependencies]
cupy = ">=13.4.1"
# jaxlib = { version = "*", build = "cuda12*" }  # unavailable
pytorch = { version = ">=2.6.0", build = "cuda12*" }

[tool.pixi.environments]
default = { features = ["py313"], solve-group = "py313" }
lint = { features = ["py313", "lint"], solve-group = "py313" }
docs = { features = ["py313", "docs"], solve-group = "py313" }
tests = { features = ["py313", "tests"], solve-group = "py313" }
tests-py313 = { features = ["py313", "tests"], solve-group = "py313" } # alias of tests

# Some backends may pin numpy; use separate solve-group
dev = { features = ["py310", "lint", "tests", "docs", "dev", "backends"], solve-group = "backends" }
tests-backends = { features = ["py310", "tests", "backends"], solve-group = "backends" }

# CUDA not available on free github actions and on some developers' PCs
dev-cuda = { features = ["py310", "lint", "tests", "docs", "dev", "backends", "cuda-backends"], solve-group = "cuda" }
tests-cuda = { features = ["py310", "tests", "backends", "cuda-backends"], solve-group = "cuda" }

# Ungrouped environments
tests-numpy1 = ["py310", "tests", "numpy1"]
tests-py310 = ["py310", "tests"]


# pytest

[tool.pytest.ini_options]
minversion = "6.0"
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
xfail_strict = true
filterwarnings = ["error"]
log_cli_level = "INFO"
testpaths = ["tests"]
markers = [
  "skip_xp_backend(library, /, *, reason=None): Skip test for a specific backend",
  "xfail_xp_backend(library, /, *, reason=None, strict=None): Xfail test for a specific backend",
]


# Coverage

[tool.coverage]
run.source = ["array_api_extra"]

# mypy

[tool.mypy]
files = ["src", "tests"]
python_version = "3.10"
warn_unused_configs = true
strict = true
enable_error_code = ["ignore-without-code", "truthy-bool"]
# https://github.com/data-apis/array-api-typing
disallow_any_expr = false
# false positives with input validation
disable_error_code = ["redundant-expr", "unreachable", "no-any-return"]

[[tool.mypy.overrides]]
# slow or unavailable on Windows; do not add to the lint env
module = ["cupy.*", "jax.*", "sparse.*", "torch.*"]
ignore_missing_imports = true

# pyright

[tool.basedpyright]
include = ["src", "tests"]
pythonVersion = "3.10"
pythonPlatform = "All"
typeCheckingMode = "all"

# https://github.com/data-apis/array-api-typing
reportAny = false
reportExplicitAny = false
# no array-api-strict type stubs; pytest fixtures
reportUnknownMemberType = false
# no array-api-compat type stubs; pytest fixtures
reportUnknownVariableType = false
# Redundant with mypy checks
reportMissingImports = false
reportMissingTypeStubs = false
# false positives for input validation
reportUnreachable = false
# ruff handles this
reportUnusedParameter = false
# cyclic imports inside function bodies
reportImportCycles = false
# PyRight can't trace types in lambdas
reportUnknownLambdaType = false

executionEnvironments = [
  { root = "tests", reportPrivateUsage = false },
  { root = "src" },
]


# Ruff

[tool.ruff]
target-version = "py310"

[tool.ruff.lint]
extend-select = [
  "B",   # flake8-bugbear
  "F",   # Pyflakes
  "I",   # isort
  "E",   # Pycodestyle
  "W",   # Pycodestyle
  "N",   # pep8-naming
  "ARG", # flake8-unused-arguments
  "C4",  # flake8-comprehensions
  "EM",  # flake8-errmsg
  "ICN", # flake8-import-conventions
  "ISC", # flake8-implicit-str-concat
  "G",   # flake8-logging-format
  "PGH", # pygrep-hooks
  "PIE", # flake8-pie
  "PL",  # pylint
  "PT",  # flake8-pytest-style
  "PTH", # flake8-use-pathlib
  "RET", # flake8-return
  "RUF", # Ruff-specific
  "SIM", # flake8-simplify
  "T20", # flake8-print
  "UP",  # pyupgrade
  "YTT", # flake8-2020
  "EXE", # flake8-executable
  "NPY", # NumPy specific rules
  "PD",  # pandas-vet
  "UP",  # Pyupgrade
]
ignore = [
  "PLR09",   # Too many <...>
  "PLR2004", # Magic value used in comparison
  "ISC001",  # Conflicts with formatter
  "N801",    # Class name should use CapWords convention
  "N802",    # Function name should be lowercase
  "N806",    # Variable in function should be lowercase
]


# Pylint

[tool.pylint]
py-version = "3.10"
reports.output-format = "colorized"
messages_control.disable = [
  "design",                     # ignore heavily opinionated design checks
  "fixme",                      # allow FIXME comments
  "line-too-long",              # ruff handles this
  "unused-argument",            # ruff handles this
  "missing-function-docstring", # numpydoc handles this
  "import-error",               # mypy handles this
  "import-outside-toplevel",    # optional dependencies
]


# numpydoc

[tool.numpydoc_validation]
checks = [
  "all",  # report on all checks, except the below
  "EX01", # most docstrings do not need an example
  "SA01", # data-apis/array-api-extra#87
  "ES01", # most docstrings do not need an extended summary
]
exclude = [ # don't report on objects that match any of these regex
  '.*test_*',
]
