Skip to content

Commit

Permalink
Forbid to use or register ClassVar, refs #2978
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Jan 7, 2022
1 parent 22607fe commit fd3874f
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 0 deletions.
11 changes: 11 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
RELEASE_TYPE: minor

This release disallows using :obj:`python:typing.ClassVar`
with :func:`~hypothesis.strategies.from_type`
and :func:`~hypothesis.strategies.register_type_strategy`.

Why?
Because ``ClassVar`` can only be used during ``class`` definition.
We don't generate class attributes.

It also does not make sense as a runtime type on its own.
11 changes: 11 additions & 0 deletions hypothesis-python/src/hypothesis/strategies/_internal/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,12 @@ def as_strategy(strat_or_callable, thing, final=True):
"Cannot resolve TypeAlias to a strategy, "
"because there are no instances of it at runtime"
)
if thing in types.ClassVarTypes:
# Code like `st.from_type(ClassVar)` does not make sense.
raise InvalidArgument(
"Cannot resolve ClassVar to a strategy, "
"because there are no instances of it at runtime"
)
# Now that we know `thing` is a type, the first step is to check for an
# explicitly registered strategy. This is the best (and hopefully most
# common) way to resolve a type to a strategy. Note that the value in the
Expand Down Expand Up @@ -1762,6 +1768,11 @@ def register_type_strategy(
f"custom_type={custom_type!r} is not allowed to be registered, "
"because there is no such thing as a runtime instance of TypeAlias"
)
elif custom_type in types.ClassVarTypes:
raise InvalidArgument(
f"custom_type={custom_type!r} is not allowed to be registered, "
"because there is no such thing as a runtime instance of ClassVar"
)
elif not (isinstance(strategy, SearchStrategy) or callable(strategy)):
raise InvalidArgument(
"strategy=%r must be a SearchStrategy, or a function that takes "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
except AttributeError: # pragma: no cover
pass # Is missing for `typing_extensions<3.10`

ClassVarTypes: tuple = (typing.ClassVar,)
try:
ClassVarTypes += (typing_extensions.ClassVar,) # type: ignore
except AttributeError:
pass # `typing_extensions` might not be installed


# We use this variable to be sure that we are working with a type from `typing`:
typing_root_type = (typing._Final, typing._GenericAlias) # type: ignore
Expand Down
17 changes: 17 additions & 0 deletions hypothesis-python/tests/typing_extensions/test_backported_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import pytest
from typing_extensions import (
Annotated,
ClassVar,
DefaultDict,
Literal,
NewType,
Expand Down Expand Up @@ -158,3 +159,19 @@ def test_type_alias_type(type_alias_type):

with pytest.raises(InvalidArgument, match="is not allowed to be registered"):
st.register_type_strategy(type_alias_type, st.none())


@pytest.mark.parametrize(
"class_var_type",
[
ClassVar,
typing.ClassVar,
],
)
def test_class_var_type(class_var_type):
strategy = st.from_type(class_var_type)
with pytest.raises(InvalidArgument, match="Cannot resolve ClassVar to a strategy"):
strategy.example()

with pytest.raises(InvalidArgument, match="is not allowed to be registered"):
st.register_type_strategy(class_var_type, st.none())

0 comments on commit fd3874f

Please sign in to comment.