diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..cf07dc6696 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,15 @@ +RELEASE_TYPE: minor + +This release makes it an error to assign ``settings = settings(...)`` +as a class attribute on a :class:`~hypothesis.stateful.RuleBasedStateMachine`. +This has never had any effect, and it should be used as a decorator instead: + +.. code-block: python + + class BadMachine(RuleBasedStateMachine): + """This doesn't do anything, and is now an error!""" + settings = settings(derandomize=True) + + @settings(derandomize=True) + class GoodMachine(RuleBasedStateMachine): + """This is the right way to do it :-)""" diff --git a/hypothesis-python/src/hypothesis/core.py b/hypothesis-python/src/hypothesis/core.py index 74e363447c..1c4cf9e144 100644 --- a/hypothesis-python/src/hypothesis/core.py +++ b/hypothesis-python/src/hypothesis/core.py @@ -610,10 +610,10 @@ def get_random_for_wrapped_test(test, wrapped_test): @attr.s class Stuff: - selfy = attr.ib(default=None) - args = attr.ib(factory=tuple) - kwargs = attr.ib(factory=dict) - given_kwargs = attr.ib(factory=dict) + selfy: Any = attr.ib(default=None) + args: tuple = attr.ib(factory=tuple) + kwargs: dict = attr.ib(factory=dict) + given_kwargs: dict = attr.ib(factory=dict) def process_arguments_to_given(wrapped_test, arguments, kwargs, given_kwargs, params): diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index d935e2e9d9..396b9498fe 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -756,7 +756,7 @@ def array_dtypes( field_names, st.tuples(field_names, field_names).filter(lambda ns: ns[0] != ns[1]), ) - elements = st.tuples(name_titles, subtype_strategy) + elements: st.SearchStrategy[tuple] = st.tuples(name_titles, subtype_strategy) if allow_subarrays: elements |= st.tuples( name_titles, subtype_strategy, array_shapes(max_dims=2, max_side=2) diff --git a/hypothesis-python/src/hypothesis/stateful.py b/hypothesis-python/src/hypothesis/stateful.py index e2b094958a..e70c805815 100644 --- a/hypothesis-python/src/hypothesis/stateful.py +++ b/hypothesis-python/src/hypothesis/stateful.py @@ -220,9 +220,10 @@ def run_state_machine(factory, data): class StateMachineMeta(type): def __setattr__(cls, name, value): if name == "settings" and isinstance(value, Settings): + descr = f"settings({value.show_changed()})" raise AttributeError( - f"Assigning {cls.__name__}.settings = {value} does nothing. Assign " - f"to {cls.__name__}.TestCase.settings, or use @{value} as a decorator " + f"Assigning {cls.__name__}.settings = {descr} does nothing. Assign " + f"to {cls.__name__}.TestCase.settings, or use @{descr} as a decorator " f"on the {cls.__name__} class." ) return super().__setattr__(name, value) @@ -255,6 +256,15 @@ def __init__(self) -> None: self._initialize_rules_to_run = copy(self.initialize_rules()) self._rules_strategy = RuleStrategy(self) + if isinstance(s := vars(type(self)).get("settings"), Settings): + tname = type(self).__name__ + descr = f"settings({s.show_changed()})" + raise InvalidDefinition( + f"Assigning settings = {descr} as a class attribute does nothing. " + f"Assign to {tname}.TestCase.settings, or use @{descr} as a decorator " + f"on the {tname} class." + ) + def _pretty_print(self, value): if isinstance(value, VarReference): return value.name diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/regex.py b/hypothesis-python/src/hypothesis/strategies/_internal/regex.py index a7f99c4ae5..23c3aedc82 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/regex.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/regex.py @@ -58,7 +58,9 @@ } -GROUP_CACHE_STRATEGY = st.shared(st.builds(dict), key="hypothesis.regex.group_cache") +GROUP_CACHE_STRATEGY: st.SearchStrategy[dict] = st.shared( + st.builds(dict), key="hypothesis.regex.group_cache" +) @st.composite diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py index 0a5fae4d9b..d979081415 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py @@ -713,7 +713,7 @@ def _networks(bits): st.binary(), st.integers(0, 255), # As with Reversible, we tuplize this for compatibility with Hashable. - st.lists(st.integers(0, 255)).map(tuple), # type: ignore + st.lists(st.integers(0, 255)).map(tuple), ), typing.BinaryIO: st.builds(io.BytesIO, st.binary()), typing.TextIO: st.builds(io.StringIO, st.text()), diff --git a/hypothesis-python/src/hypothesis/vendor/tlds-alpha-by-domain.txt b/hypothesis-python/src/hypothesis/vendor/tlds-alpha-by-domain.txt index 931aeb432b..5402c8d9cb 100644 --- a/hypothesis-python/src/hypothesis/vendor/tlds-alpha-by-domain.txt +++ b/hypothesis-python/src/hypothesis/vendor/tlds-alpha-by-domain.txt @@ -1,4 +1,4 @@ -# Version 2023111100, Last Updated Sat Nov 11 07:07:01 2023 UTC +# Version 2023111800, Last Updated Sat Nov 18 07:07:01 2023 UTC AAA AARP ABB @@ -379,7 +379,6 @@ ES ESQ ESTATE ET -ETISALAT EU EUROVISION EUS @@ -1370,7 +1369,6 @@ XN--MGB9AWBF XN--MGBA3A3EJT XN--MGBA3A4F16A XN--MGBA7C0BBN0A -XN--MGBAAKC7DVF XN--MGBAAM7A8H XN--MGBAB2BD XN--MGBAH1A3HJKRD diff --git a/hypothesis-python/tests/cover/test_stateful.py b/hypothesis-python/tests/cover/test_stateful.py index bd3bbce214..191c2d22a9 100644 --- a/hypothesis-python/tests/cover/test_stateful.py +++ b/hypothesis-python/tests/cover/test_stateful.py @@ -1215,3 +1215,19 @@ def test_min_steps_argument(): # (oh, and it's OK if you ask for more than we're actually going to take) run_state_machine_as_test(MinStepsMachine, _min_steps=20) + + +class ErrorsOnClassAttributeSettings(RuleBasedStateMachine): + settings = Settings(derandomize=True) + + @rule() + def step(self): + pass + + +def test_fails_on_settings_class_attribute(): + with pytest.raises( + InvalidDefinition, + match="Assigning .+ as a class attribute does nothing", + ): + run_state_machine_as_test(ErrorsOnClassAttributeSettings) diff --git a/requirements/coverage.txt b/requirements/coverage.txt index 38723a520b..d53cd3d5fc 100644 --- a/requirements/coverage.txt +++ b/requirements/coverage.txt @@ -10,7 +10,7 @@ async-timeout==4.0.3 # via redis attrs==23.1.0 # via hypothesis (hypothesis-python/setup.py) -black==23.10.1 +black==23.11.0 # via -r requirements/coverage.in click==8.1.7 # via @@ -38,7 +38,7 @@ mypy-extensions==1.0.0 # via # black # typing-inspect -numpy==1.26.1 +numpy==1.26.2 # via # -r requirements/coverage.in # pandas @@ -46,13 +46,13 @@ packaging==23.2 # via # black # pytest -pandas==2.1.2 +pandas==2.1.3 # via -r requirements/coverage.in pathspec==0.11.2 # via black pexpect==4.8.0 # via -r requirements/test.in -platformdirs==3.11.0 +platformdirs==4.0.0 # via black pluggy==1.3.0 # via pytest @@ -62,7 +62,7 @@ pytest==7.4.3 # via # -r requirements/test.in # pytest-xdist -pytest-xdist==3.3.1 +pytest-xdist==3.4.0 # via -r requirements/test.in python-dateutil==2.8.2 # via diff --git a/requirements/fuzzing.txt b/requirements/fuzzing.txt index c3cca3f3b2..a2acba994c 100644 --- a/requirements/fuzzing.txt +++ b/requirements/fuzzing.txt @@ -4,6 +4,8 @@ # # ./build.sh upgrade-requirements # +annotated-types==0.6.0 + # via -r requirements/coverage.in ansi2html==1.8.0 # via dash async-timeout==4.0.3 @@ -12,14 +14,14 @@ attrs==23.1.0 # via # hypothesis # hypothesis (hypothesis-python/setup.py) -black==23.10.1 +black==23.11.0 # via # -r requirements/coverage.in # hypofuzz # hypothesis blinker==1.7.0 # via flask -certifi==2023.7.22 +certifi==2023.11.17 # via requests charset-normalizer==3.3.2 # via requests @@ -56,7 +58,7 @@ flask==3.0.0 # via dash hypofuzz==23.7.1 # via -r requirements/fuzzing.in -hypothesis[cli]==6.88.1 +hypothesis[cli]==6.89.0 # via # hypofuzz # hypothesis @@ -90,7 +92,7 @@ mypy-extensions==1.0.0 # typing-inspect nest-asyncio==1.5.8 # via dash -numpy==1.26.1 +numpy==1.26.2 # via # -r requirements/coverage.in # pandas @@ -99,7 +101,7 @@ packaging==23.2 # black # plotly # pytest -pandas==2.1.2 +pandas==2.1.3 # via # -r requirements/coverage.in # hypofuzz @@ -107,7 +109,7 @@ pathspec==0.11.2 # via black pexpect==4.8.0 # via -r requirements/test.in -platformdirs==3.11.0 +platformdirs==4.0.0 # via black plotly==5.18.0 # via dash @@ -117,14 +119,14 @@ psutil==5.9.6 # via hypofuzz ptyprocess==0.7.0 # via pexpect -pygments==2.16.1 +pygments==2.17.0 # via rich pytest==7.4.3 # via # -r requirements/test.in # hypofuzz # pytest-xdist -pytest-xdist==3.3.1 +pytest-xdist==3.4.0 # via -r requirements/test.in python-dateutil==2.8.2 # via @@ -144,7 +146,7 @@ requests==2.31.0 # hypofuzz retrying==1.3.4 # via dash -rich==13.6.0 +rich==13.7.0 # via hypothesis six==1.16.0 # via @@ -172,7 +174,7 @@ typing-inspect==0.9.0 # via libcst tzdata==2023.3 # via pandas -urllib3==2.0.7 +urllib3==2.1.0 # via requests werkzeug==3.0.1 # via diff --git a/requirements/test.txt b/requirements/test.txt index 3f24bf6083..55d412ba49 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -26,7 +26,7 @@ pytest==7.4.3 # via # -r requirements/test.in # pytest-xdist -pytest-xdist==3.3.1 +pytest-xdist==3.4.0 # via -r requirements/test.in sortedcontainers==2.4.0 # via hypothesis (hypothesis-python/setup.py) diff --git a/requirements/tools.txt b/requirements/tools.txt index 435a35d353..5d671f467a 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -18,13 +18,13 @@ babel==2.13.1 # via sphinx beautifulsoup4==4.12.2 # via sphinx-codeautolink -black==23.10.1 +black==23.11.0 # via shed build==1.0.3 # via pip-tools cachetools==5.3.2 # via tox -certifi==2023.7.22 +certifi==2023.11.17 # via requests cffi==1.16.0 # via cryptography @@ -98,7 +98,7 @@ jeepney==0.8.0 # secretstorage jinja2==3.1.2 # via sphinx -keyring==24.2.0 +keyring==24.3.0 # via twine lark==1.1.8 # via -r requirements/tools.in @@ -116,7 +116,7 @@ mdurl==0.1.2 # via markdown-it-py more-itertools==10.1.0 # via jaraco-classes -mypy==1.6.1 +mypy==1.7.0 # via -r requirements/tools.in mypy-extensions==1.0.0 # via @@ -154,7 +154,7 @@ pluggy==1.3.0 # via # pytest # tox -prompt-toolkit==3.0.39 +prompt-toolkit==3.0.41 # via ipython ptyprocess==0.7.0 # via pexpect @@ -164,7 +164,7 @@ pycparser==2.21 # via cffi pyflakes==3.1.0 # via autoflake -pygments==2.16.1 +pygments==2.17.0 # via # ipython # readme-renderer @@ -174,7 +174,7 @@ pyproject-api==1.6.1 # via tox pyproject-hooks==1.0.0 # via build -pyright==1.1.334 +pyright==1.1.336 # via -r requirements/tools.in pytest==7.4.3 # via -r requirements/tools.in @@ -198,9 +198,9 @@ restructuredtext-lint==1.4.0 # via -r requirements/tools.in rfc3986==2.0.0 # via twine -rich==13.6.0 +rich==13.7.0 # via twine -ruff==0.1.3 +ruff==0.1.6 # via -r requirements/tools.in secretstorage==3.3.3 # via keyring @@ -285,7 +285,7 @@ types-pyopenssl==23.3.0.0 # via types-redis types-pytz==2023.3.1.1 # via -r requirements/tools.in -types-redis==4.6.0.9 +types-redis==4.6.0.10 # via -r requirements/tools.in typing-extensions==4.8.0 # via @@ -297,13 +297,13 @@ typing-extensions==4.8.0 # typing-inspect typing-inspect==0.9.0 # via libcst -urllib3==2.0.7 +urllib3==2.1.0 # via # requests # twine virtualenv==20.24.6 # via tox -wcwidth==0.2.9 +wcwidth==0.2.10 # via prompt-toolkit wheel==0.41.3 # via pip-tools diff --git a/whole-repo-tests/test_mypy.py b/whole-repo-tests/test_mypy.py index 0bf11e7399..d81f8f3fb2 100644 --- a/whole-repo-tests/test_mypy.py +++ b/whole-repo-tests/test_mypy.py @@ -102,14 +102,13 @@ def convert_lines(): ("dictionaries(integers(), datetimes())", "dict[int, datetime.datetime]"), ("data()", "hypothesis.strategies._internal.core.DataObject"), ("none() | integers()", "Union[None, int]"), - # Ex`-1 stands for recursion in the whole type, i.e. Ex`0 == Union[...] - ("recursive(integers(), lists)", "Union[list[Ex`-1], int]"), + ("recursive(integers(), lists)", "Union[list[Any], int]"), # We have overloads for up to five types, then fall back to Any. # (why five? JSON atoms are None|bool|int|float|str and we do that a lot) ("one_of(integers(), text())", "Union[int, str]"), ( "one_of(integers(), text(), none(), binary(), builds(list))", - "Union[int, str, None, bytes, list[_T`1]]", + "Union[int, str, None, bytes, list[Never]]", ), ( "one_of(integers(), text(), none(), binary(), builds(list), builds(dict))", @@ -126,10 +125,6 @@ def convert_lines(): "tuples(text(), text(), text(), text(), text(), text())", "tuple[Any, ...]", ), - ( - "from_type(type).flatmap(from_type).filter(lambda x: not isinstance(x, int))", - "Ex_Inv`-1", - ), ], ) def test_revealed_types(tmpdir, val, expect): @@ -137,8 +132,7 @@ def test_revealed_types(tmpdir, val, expect): f = tmpdir.join(expect + ".py") f.write( "from hypothesis.strategies import *\n" - f"s = {val}\n" - "reveal_type(s)\n" # fmt: skip + f"reveal_type({val})\n" # fmt: skip ) typ = get_mypy_analysed_type(str(f.realpath()), val) assert typ == f"SearchStrategy[{expect}]"