Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Allow registration of tuples #3782

Closed

Conversation

nickcollins
Copy link
Contributor

Previously, if tuple was registered, the registered strategy would be ignored. Now it is actually used. Note that tuple[...] cannot be registered because genericized types are not allowed to be registered in general, so a user must register tuple with a function that takes the type args and returns a strategy appropriate to the particular genericized type at hand.

Previously, if `tuple` was registered, the registered strategy would be
ignored. Now it is actually used. Note that `tuple[...]` cannot be
registered because genericized types are not allowed to be registered in
general, so a user must register `tuple` with a function that takes the
type args and returns a strategy appropriate to the particular
genericized type at hand.
@nickcollins
Copy link
Contributor Author

well it worked on my machine ....
maybe i'm not running all the right tests?

Copy link
Member

@Zac-HD Zac-HD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like dropping some obsolete compatibility code will make this easier - let me know if you'd like me to push that, since I'm probably the only person who remembers how it worked. I think all the tests pass on Python 3.10 and later, which is a good sign!

Otherwise, see comments below and don't forget to write a RELEASE.rst note 😄

@@ -118,6 +118,14 @@ def test_lookup_overrides_defaults(typ):
assert st.from_type(typ).example() is not sentinel


def test_lookup_registered_tuple():
sentinel = object()
typ = tuple[int]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail on Python 3.8, because builtin generics only became subscriptable in Python 3.9. The solution is to move this test to hypothesis-python/tests/cover/test_lookup_py39.py 🙂

# you can register types with all generic parameters
List[T],
Sequence[T],
tuple[T],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from typing import Tuple here; and it'd be worth testing both Tuple[T] and Tuple[T, ...].

We may need to wrap it in pytest.param(Tuple[T], marks=pytest.mark.xfail(sys.version_info[:2] <= (3, 9), reason="stdlib changes") to fix the other CI failures though, because it's treated as a parametrized generic type on those older versions.

@@ -1227,25 +1227,6 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]:
# refactoring it's hard to do without creating circular imports.
from hypothesis.strategies._internal import types

def as_strategy(strat_or_callable, thing):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to avoid moving this, since we won't need it for a registered function to resolve tuples.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we still need it for tuples though? As I understand it sanity checks the user registration. Like we might take it for granted that the user registration ought to be callable, but even if it's confirmed to be callable, as_strategy still does other checks to ensure that the returned strategy is formed correctly, and it feels right to invoke as_strategy to perform those checks rather than to copy the code.

I could try leaving as_strategy in core.py, and then if a circular dependency comes up when I invoke it in types.py, I could try finding some way to resolve that. I did not actually verify that this causes a circular dependency, I just assumed that it would, so I could go check that. But if I don't use as_strategy at all, then I guess I'd have to copy some of its checks into types.py, which I could do if you want but doesn't seem like the best solution to me.

Comment on lines 335 to 337
if get_origin(thing) == tuple or isinstance(
thing, getattr(typing, "TupleMeta", ())
):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that the TupleMeta case hasn't been necessary since we dropped support for Python 3.6, which means we can move this logic to a new @register'd resolve_tuple() function below. That will treat tuple as if it's any other generic type, and should simplify things considerably!

Copy link
Contributor Author

@nickcollins nickcollins Nov 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, I'm just pre-registering lambda _: NotImplmented, rather than going the route i mentioned earlier of pre-registering the default tuple resolution logic. Are you suggesting I go back to that option? And you're saying that some logic in from_typing_type will handle tuples, if we delete the tuple special casing from from_typing_type?

I'm looking at from_typing_type and the only place where it checks global_type_lookup is at

mapping = {
k: v
for k, v in _global_type_lookup.items()
if is_generic_type(k) and try_issubclass(k, thing)
}

but that check requires is_generic_type(k) which I assume is not true for k == tuple?

@nickcollins
Copy link
Contributor Author

It looks like dropping some obsolete compatibility code will make this easier - let me know if you'd like me to push that
Yea, that sounds great if convenient!

@Zac-HD
Copy link
Member

Zac-HD commented Nov 5, 2023

Wow - #3785 took me a few attempts, but it looks like dropping the obsolete special cases was all we needed to fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants