1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis/
#
# Copyright the Hypothesis Authors.
# Individual contributors are listed in AUTHORS.rst and the git log.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
from types import SimpleNamespace
from weakref import WeakValueDictionary
import pytest
from hypothesis.control import settings
from hypothesis.extra import array_api
from hypothesis.extra.array_api import (
NOMINAL_VERSIONS,
make_strategies_namespace,
mock_xp,
)
from hypothesis.strategies import SearchStrategy
pytestmark = [
pytest.mark.filterwarnings("ignore::hypothesis.errors.HypothesisWarning"),
pytest.mark.skipif(
settings._current_profile == "threading",
reason="make_strategies_namespace is not thread safe",
),
]
class HashableArrayModuleFactory:
"""
mock_xp cannot be hashed and thus cannot be used in our cache. So just for
the purposes of testing the cache, we wrap it with an unsafe hash method.
"""
def __getattr__(self, name):
return getattr(mock_xp, name)
def __hash__(self):
return hash(tuple(sorted(mock_xp.__dict__)))
@pytest.mark.parametrize("api_version", ["2021.12", None])
def test_caching(api_version, monkeypatch):
"""Caches namespaces respective to arguments."""
xp = HashableArrayModuleFactory()
assert isinstance(array_api._args_to_xps, WeakValueDictionary) # sanity check
monkeypatch.setattr(array_api, "_args_to_xps", WeakValueDictionary())
assert len(array_api._args_to_xps) == 0 # sanity check
xps1 = array_api.make_strategies_namespace(xp, api_version=api_version)
assert len(array_api._args_to_xps) == 1
xps2 = array_api.make_strategies_namespace(xp, api_version=api_version)
assert len(array_api._args_to_xps) == 1
assert isinstance(xps2, SimpleNamespace)
assert xps2 is xps1
del xps1
del xps2
assert len(array_api._args_to_xps) == 0
@pytest.mark.parametrize(
"api_version1, api_version2", [(None, "2021.12"), ("2021.12", None)]
)
def test_inferred_namespace_shares_cache(api_version1, api_version2, monkeypatch):
"""Results from inferred versions share the same cache key as results
from specified versions."""
xp = HashableArrayModuleFactory()
xp.__array_api_version__ = "2021.12"
assert isinstance(array_api._args_to_xps, WeakValueDictionary) # sanity check
monkeypatch.setattr(array_api, "_args_to_xps", WeakValueDictionary())
assert len(array_api._args_to_xps) == 0 # sanity check
xps1 = array_api.make_strategies_namespace(xp, api_version=api_version1)
assert xps1.api_version == "2021.12" # sanity check
assert len(array_api._args_to_xps) == 1
xps2 = array_api.make_strategies_namespace(xp, api_version=api_version2)
assert xps2.api_version == "2021.12" # sanity check
assert len(array_api._args_to_xps) == 1
assert xps2 is xps1
def test_complex_dtypes_raises_on_2021_12():
"""Accessing complex_dtypes() for 2021.12 strategy namespace raises helpful
error, but accessing on future versions returns expected strategy."""
first_xps = make_strategies_namespace(mock_xp, api_version="2021.12")
with pytest.raises(AttributeError, match="attempted to access"):
first_xps.complex_dtypes()
for api_version in NOMINAL_VERSIONS[1:]:
xps = make_strategies_namespace(mock_xp, api_version=api_version)
assert isinstance(xps.complex_dtypes(), SearchStrategy)
|