├── .flake8 ├── .gitattributes ├── .github └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── LICENSE ├── MANIFEST.in ├── README.md ├── _config.yml ├── array-api-strict-skips.txt ├── array_api_tests ├── __init__.py ├── _array_module.py ├── _version.py ├── algos.py ├── array_helpers.py ├── dtype_helpers.py ├── hypothesis_helpers.py ├── pytest_helpers.py ├── shape_helpers.py ├── stubs.py ├── test_array_object.py ├── test_constants.py ├── test_creation_functions.py ├── test_data_type_functions.py ├── test_fft.py ├── test_has_names.py ├── test_indexing_functions.py ├── test_inspection_functions.py ├── test_linalg.py ├── test_manipulation_functions.py ├── test_operators_and_elementwise_functions.py ├── test_searching_functions.py ├── test_set_functions.py ├── test_signatures.py ├── test_sorting_functions.py ├── test_special_cases.py ├── test_statistical_functions.py ├── test_utility_functions.py └── typing.py ├── conftest.py ├── meta_tests ├── README.md ├── __init__.py ├── test_array_helpers.py ├── test_broadcasting.py ├── test_equality_mapping.py ├── test_hypothesis_helpers.py ├── test_linalg.py ├── test_partial_adopters.py ├── test_pytest_helpers.py ├── test_signatures.py ├── test_special_cases.py └── test_utils.py ├── pytest.ini ├── reporting.py ├── requirements.txt └── setup.cfg /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | select = F 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | array_api_tests/_version.py} export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Set up Python ${{ matrix.python-version }} 12 | uses: actions/setup-python@v5 13 | with: 14 | python-version: "3.10" 15 | - name: Run pre-commit hook 16 | uses: pre-commit/action@v3.0.1 17 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test Array API Strict 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ["3.10", "3.11"] 12 | 13 | steps: 14 | - name: Checkout array-api-tests 15 | uses: actions/checkout@v1 16 | with: 17 | submodules: 'true' 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v1 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | python -m pip install array-api-strict 26 | python -m pip install -r requirements.txt 27 | - name: Run the test suite 28 | env: 29 | ARRAY_API_TESTS_MODULE: array_api_strict 30 | ARRAY_API_STRICT_API_VERSION: 2024.12 31 | run: | 32 | pytest -v -rxXfE --skips-file array-api-strict-skips.txt array_api_tests/ 33 | # We also have internal tests that isn't really necessary for adopters 34 | pytest -v -rxXfE meta_tests/ 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # IDE 121 | .idea/ 122 | .vscode/ 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytest-json-report 136 | .report.json 137 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "array_api_tests/array-api"] 2 | path = array-api 3 | url = https://github.com/data-apis/array-api/ 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/flake8 3 | rev: '4.0.1' 4 | hooks: 5 | - id: flake8 6 | args: [--select, F] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Consortium for Python Data API Standards contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include array_api_tests/_version.py} 3 | include array_api_tests/_version.py 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Test Suite for Array API Compliance 2 | 3 | This is the test suite for array libraries adopting the [Python Array API 4 | standard](https://data-apis.org/array-api/latest). 5 | 6 | Keeping full coverage of the spec is an on-going priority as the Array API evolves. 7 | Feedback and contributions are welcome! 8 | 9 | ## Quickstart 10 | 11 | ### Setup 12 | 13 | Currently we pin the Array API specification repo [`array-api`](https://github.com/data-apis/array-api/) 14 | as a git submodule. This might change in the future to better support vendoring 15 | use cases (see [#107](https://github.com/data-apis/array-api-tests/issues/107)), 16 | but for now be sure submodules are pulled too, e.g. 17 | 18 | ```bash 19 | $ git submodule update --init 20 | ``` 21 | 22 | To run the tests, install the testing dependencies. 23 | 24 | ```bash 25 | $ pip install -r requirements.txt 26 | ``` 27 | 28 | Ensure you have the array library that you want to test installed. 29 | 30 | ### Specifying the array module 31 | 32 | You need to specify the array library to test. It can be specified via the 33 | `ARRAY_API_TESTS_MODULE` environment variable, e.g. 34 | 35 | ```bash 36 | $ export ARRAY_API_TESTS_MODULE=array_api_strict 37 | ``` 38 | 39 | To specify a runtime-defined module, define `xp` using the `exec('...')` syntax: 40 | 41 | ```bash 42 | $ export ARRAY_API_TESTS_MODULE="exec('import quantity_array, numpy; xp = quantity_array.quantity_namespace(numpy)')" 43 | ``` 44 | 45 | Alternately, import/define the `xp` variable in `array_api_tests/__init__.py`. 46 | 47 | ### Specifying the API version 48 | 49 | You can specify the API version to use when testing via the 50 | `ARRAY_API_TESTS_VERSION` environment variable, e.g. 51 | 52 | ```bash 53 | $ export ARRAY_API_TESTS_VERSION="2023.12" 54 | ``` 55 | 56 | Currently this defaults to the array module's `__array_api_version__` value, and 57 | if that attribute doesn't exist then we fallback to `"2021.12"`. 58 | 59 | ### Run the suite 60 | 61 | Simply run `pytest` against the `array_api_tests/` folder to run the full suite. 62 | 63 | ```bash 64 | $ pytest array_api_tests/ 65 | ``` 66 | 67 | The suite tries to logically organise its tests. `pytest` allows you to only run 68 | a specific test case, which is useful when developing functions. 69 | 70 | ```bash 71 | $ pytest array_api_tests/test_creation_functions.py::test_zeros 72 | ``` 73 | 74 | ## What the test suite covers 75 | 76 | We are interested in array libraries conforming to the 77 | [spec](https://data-apis.org/array-api/latest/API_specification/index.html). 78 | Ideally this means that if a library has fully adopted the Array API, the test 79 | suite passes. We take great care to _not_ test things which are out-of-scope, 80 | so as to not unexpectedly fail the suite. 81 | 82 | ### Primary tests 83 | 84 | Every function—including array object methods—has a respective test 85 | method1. We use 86 | [Hypothesis](https://hypothesis.readthedocs.io/en/latest/) 87 | to generate a diverse set of valid inputs. This means array inputs will cover 88 | different dtypes and shapes, as well as contain interesting elements. These 89 | examples generate with interesting arrangements of non-array positional 90 | arguments and keyword arguments. 91 | 92 | Each test case will cover the following areas if relevant: 93 | 94 | * **Smoking**: We pass our generated examples to all functions. As these 95 | examples solely consist of *valid* inputs, we are testing that functions can 96 | be called using their documented inputs without raising errors. 97 | 98 | * **Data type**: For functions returning/modifying arrays, we assert that output 99 | arrays have the correct data types. Most functions 100 | [type-promote](https://data-apis.org/array-api/latest/API_specification/type_promotion.html) 101 | input arrays and some functions have bespoke rules—in both cases we simulate 102 | the correct behaviour to find the expected data types. 103 | 104 | * **Shape**: For functions returning/modifying arrays, we assert that output 105 | arrays have the correct shape. Most functions 106 | [broadcast](https://data-apis.org/array-api/latest/API_specification/broadcasting.html) 107 | input arrays and some functions have bespoke rules—in both cases we simulate 108 | the correct behaviour to find the expected shapes. 109 | 110 | * **Values**: We assert output values (including the elements of 111 | returned/modified arrays) are as expected. Except for manipulation functions 112 | or special cases, the spec allows floating-point inputs to have inexact 113 | outputs, so with such examples we only assert values are roughly as expected. 114 | 115 | ### Additional tests 116 | 117 | In addition to having one test case for each function, we test other properties 118 | of the functions and some miscellaneous things. 119 | 120 | * **Special cases**: For functions with special case behaviour, we assert that 121 | these functions return the correct values. 122 | 123 | * **Signatures**: We assert functions have the correct signatures. 124 | 125 | * **Constants**: We assert that 126 | [constants](https://data-apis.org/array-api/latest/API_specification/constants.html) 127 | behave expectedly, are roughly the expected value, and that any related 128 | functions interact with them correctly. 129 | 130 | Be aware that some aspects of the spec are impractical or impossible to actually 131 | test, so they are not covered in the suite. 132 | 133 | ## Interpreting errors 134 | 135 | First and foremost, note that most tests have to assume that certain aspects of 136 | the Array API have been correctly adopted, as fundamental APIs such as array 137 | creation and equalities are hard requirements for many assertions. This means a 138 | test case for one function might fail because another function has bugs or even 139 | no implementation. 140 | 141 | This means adopting libraries at first will result in a vast number of errors 142 | due to cascading errors. Generally the nature of the spec means many granular 143 | details such as type promotion is likely going to also fail nearly-conforming 144 | functions. 145 | 146 | We hope to improve user experience in regards to "noisy" errors in 147 | [#51](https://github.com/data-apis/array-api-tests/issues/51). For now, if an 148 | error message involves `_UndefinedStub`, it means an attribute of the array 149 | library (including functions) and it's objects (e.g. the array) is missing. 150 | 151 | The spec is the suite's source of truth. If the suite appears to assume 152 | behaviour different from the spec, or test something that is not documented, 153 | this is a bug—please [report such 154 | issues](https://github.com/data-apis/array-api-tests/issues/) to us. 155 | 156 | 157 | ## Running on CI 158 | 159 | See our existing [GitHub Actions workflow for `array-api-strict`](https://github.com/data-apis/array-api-tests/blob/master/.github/workflows/test.yml) 160 | for an example of using the test suite on CI. Note [`array-api-strict`](https://github.com/data-apis/array-api-strict) 161 | is an implementation of the array API that uses NumPy under the hood. 162 | 163 | ### Releases 164 | 165 | We recommend pinning against a [release tag](https://github.com/data-apis/array-api-tests/releases) 166 | when running on CI. 167 | 168 | We use [calender versioning](https://calver.org/) for the releases. You should 169 | expect that any version may be "breaking" compared to the previous one, in that 170 | new tests (or improvements to existing tests) may cause a previously passing 171 | library to fail. 172 | 173 | ### Configuration 174 | 175 | #### Data-dependent shapes 176 | 177 | Use the `--disable-data-dependent-shapes` flag to skip testing functions which have 178 | [data-dependent shapes](https://data-apis.org/array-api/latest/design_topics/data_dependent_output_shapes.html). 179 | 180 | #### Extensions 181 | 182 | By default, tests for the optional Array API extensions such as 183 | [`linalg`](https://data-apis.org/array-api/latest/extensions/linear_algebra_functions.html) 184 | will be skipped if not present in the specified array module. You can purposely 185 | skip testing extension(s) via the `--disable-extension` option. 186 | 187 | #### Skip or XFAIL test cases 188 | 189 | Test cases you want to skip can be specified in a skips or XFAILS file. The 190 | difference between skip and XFAIL is that XFAIL tests are still run and 191 | reported as XPASS if they pass. 192 | 193 | By default, the skips and xfails files are `skips.txt` and `fails.txt` in the root 194 | of this repository, but any file can be specified with the `--skips-file` and 195 | `--xfails-file` command line flags. 196 | 197 | The files should list the test ids to be skipped/xfailed. Empty lines and 198 | lines starting with `#` are ignored. The test id can be any substring of the 199 | test ids to skip/xfail. 200 | 201 | ``` 202 | # skips.txt or xfails.txt 203 | # Line comments can be denoted with the hash symbol (#) 204 | 205 | # Skip specific test case, e.g. when argsort() does not respect relative order 206 | # https://github.com/numpy/numpy/issues/20778 207 | array_api_tests/test_sorting_functions.py::test_argsort 208 | 209 | # Skip specific test case parameter, e.g. you forgot to implement in-place adds 210 | array_api_tests/test_add[__iadd__(x1, x2)] 211 | array_api_tests/test_add[__iadd__(x, s)] 212 | 213 | # Skip module, e.g. when your set functions treat NaNs as non-distinct 214 | # https://github.com/numpy/numpy/issues/20326 215 | array_api_tests/test_set_functions.py 216 | ``` 217 | 218 | Here is an example GitHub Actions workflow file, where the xfails are stored 219 | in `array-api-tests.xfails.txt` in the base of the `your-array-library` repo. 220 | 221 | If you want, you can use `-o xfail_strict=True`, which causes XPASS tests (XFAIL 222 | tests that actually pass) to fail the test suite. However, be aware that 223 | XFAILures can be flaky (see below, so this may not be a good idea unless you 224 | use some other mitigation of such flakyness). 225 | 226 | If you don't want this behavior, you can remove it, or use `--skips-file` 227 | instead of `--xfails-file`. 228 | 229 | ```yaml 230 | # ./.github/workflows/array_api.yml 231 | jobs: 232 | tests: 233 | runs-on: ubuntu-latest 234 | strategy: 235 | matrix: 236 | python-version: ['3.8', '3.9', '3.10', '3.11'] 237 | 238 | steps: 239 | - name: Checkout 240 | uses: actions/checkout@v3 241 | with: 242 | path: your-array-library 243 | 244 | - name: Checkout array-api-tests 245 | uses: actions/checkout@v3 246 | with: 247 | repository: data-apis/array-api-tests 248 | submodules: 'true' 249 | path: array-api-tests 250 | 251 | - name: Run the array API test suite 252 | env: 253 | ARRAY_API_TESTS_MODULE: your.array.api.namespace 254 | run: | 255 | export PYTHONPATH="${GITHUB_WORKSPACE}/your-array-library" 256 | cd ${GITHUB_WORKSPACE}/array-api-tests 257 | pytest -v -rxXfE --ci --xfails-file ${GITHUB_WORKSPACE}/your-array-library/array-api-tests-xfails.txt array_api_tests/ 258 | ``` 259 | 260 | > **Warning** 261 | > 262 | > XFAIL tests that use Hypothesis (basically every test in the test suite except 263 | > those in test_has_names.py) can be flaky, due to the fact that Hypothesis 264 | > might not always run the test with an input that causes the test to fail. 265 | > There are several ways to avoid this problem: 266 | > 267 | > - Increase the maximum number of examples, e.g., by adding `--max-examples 268 | > 200` to the test command (the default is `20`, see below). This will 269 | > make it more likely that the failing case will be found, but it will also 270 | > make the tests take longer to run. 271 | > - Don't use `-o xfail_strict=True`. This will make it so that if an XFAIL 272 | > test passes, it will alert you in the test summary but will not cause the 273 | > test run to register as failed. 274 | > - Use skips instead of XFAILS. The difference between XFAIL and skip is that 275 | > a skipped test is never run at all, whereas an XFAIL test is always run 276 | > but ignored if it fails. 277 | > - Save the [Hypothesis examples 278 | > database](https://hypothesis.readthedocs.io/en/latest/database.html) 279 | > persistently on CI. That way as soon as a run finds one failing example, 280 | > it will always re-run future runs with that example. But note that the 281 | > Hypothesis examples database may be cleared when a new version of 282 | > Hypothesis or the test suite is released. 283 | 284 | #### Max examples 285 | 286 | The tests make heavy use 287 | [Hypothesis](https://hypothesis.readthedocs.io/en/latest/). You can configure 288 | how many examples are generated using the `--max-examples` flag, which 289 | defaults to `20`. Lower values can be useful for quick checks, and larger 290 | values should result in more rigorous runs. For example, `--max-examples 291 | 10_000` may find bugs where default runs don't but will take much longer to 292 | run. 293 | 294 | #### Skipping Dtypes 295 | 296 | The test suite will automatically skip testing of inessential dtypes if they 297 | are not present on the array module namespace, but dtypes can also be skipped 298 | manually by setting the environment variable `ARRAY_API_TESTS_SKIP_DTYPES` to 299 | a comma separated list of dtypes to skip. For example 300 | 301 | ``` 302 | ARRAY_API_TESTS_SKIP_DTYPES=uint16,uint32,uint64 pytest array_api_tests/ 303 | ``` 304 | 305 | Note that skipping certain essential dtypes such as `bool` and the default 306 | floating-point dtype is not supported. 307 | 308 | #### Turning xfails into skips 309 | 310 | Keeping a large number of ``xfails`` can have drastic effects on the run time. This is due 311 | to the way `hypothesis` works: when it detects a failure, it does a large amount 312 | of work to simplify the failing example. 313 | If the run time of the test suite becomes a problem, you can use the 314 | ``ARRAY_API_TESTS_XFAIL_MARK`` environment variable: setting it to ``skip`` skips the 315 | entries from the ``xfail.txt`` file instead of xfailing them. Anecdotally, we saw 316 | speed-ups by a factor of 4-5---which allowed us to use 4-5 larger values of 317 | ``--max-examples`` within the same time budget. 318 | 319 | #### Limiting the array sizes 320 | 321 | The test suite generates random arrays as inputs to functions it tests. "unvectorized" 322 | tests iterate over elements of arrays, which might be slow. If the run time becomes 323 | a problem, you can limit the maximum number of elements in generated arrays by 324 | setting the environment variable ``ARRAY_API_TESTS_MAX_ARRAY_SIZE`` to the 325 | desired value. By default, it is set to 1024. 326 | 327 | 328 | ## Contributing 329 | 330 | ### Remain in-scope 331 | 332 | It is important that every test only uses APIs that are part of the standard. 333 | For instance, when creating input arrays you should only use the [array creation 334 | functions](https://data-apis.org/array-api/latest/API_specification/creation_functions.html) 335 | that are documented in the spec. The same goes for testing arrays—you'll find 336 | many utilities that parralel NumPy's own test utils in the `*_helpers.py` files. 337 | 338 | ### Tools 339 | 340 | Hypothesis should almost always be used for the primary tests, and can be useful 341 | elsewhere. Effort should be made so drawn arguments are labeled with their 342 | respective names. For 343 | [`st.data()`](https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.data), 344 | draws should be accompanied with the `label` kwarg i.e. `data.draw(, 345 | label=