Repository: HypothesisWorks/hypothesis

Issue:
Ghostwriter unaware of imports for regex strategies
Not sure if this is a bug or a missing feature, but when using the ghostwriter on the following code

```project/minimal_example.py
import re
from typing import NewType

from hypothesis.strategies import register_type_strategy
from hypothesis import strategies as st


TestType = NewType('TestType', str)

register_type_strategy(TestType, st.from_regex(re.compile(".+")))

def something(t: TestType):
    pass
```


the following broken code is generated

```py
# This test code was written by the `hypothesis.extra.ghostwriter` module
# and is provided under the Creative Commons Zero public domain dedication.

import project.minimal_example
from hypothesis import given, strategies as st

# TODO: replace st.nothing() with an appropriate strategy


@given(x=st.nothing())
def test_fuzz_TestType(x):
    project.minimal_example.TestType(x=x)


@given(t=st.from_regex(re.compile(r".+", re.UNICODE)))
def test_fuzz_something(t):
    project.minimal_example.something(t=t)
```

Maybe calling `_imports_for_object` from `_imports_for_strategy` for `from_regex` strategies would be a solution, but I have no idea how to add that case inside the `LazyStrategy` branch.




Discussion:

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/extra/ghostwriter.py b/hypothesis-python/src/hypothesis/extra/ghostwriter.py
--- a/hypothesis-python/src/hypothesis/extra/ghostwriter.py
+++ b/hypothesis-python/src/hypothesis/extra/ghostwriter.py
@@ -597,8 +597,16 @@ def _imports_for_strategy(strategy):
     # If we have a lazy from_type strategy, because unwrapping it gives us an
     # error or invalid syntax, import that type and we're done.
     if isinstance(strategy, LazyStrategy):
-        if strategy.function is st.from_type:
-            return _imports_for_object(strategy._LazyStrategy__args[0])
+        if strategy.function.__name__ in (
+            st.from_type.__name__,
+            st.from_regex.__name__,
+        ):
+            return {
+                imp
+                for arg in set(strategy._LazyStrategy__args)
+                | set(strategy._LazyStrategy__kwargs.values())
+                for imp in _imports_for_object(arg)
+            }
         elif _get_module(strategy.function).startswith("hypothesis.extra."):
             return {(_get_module(strategy.function), strategy.function.__name__)}
             module = _get_module(strategy.function).replace("._array_helpers", ".numpy")


Test patch:
diff --git a/hypothesis-python/tests/ghostwriter/test_ghostwriter.py b/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
--- a/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
+++ b/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
@@ -42,6 +42,7 @@
 from hypothesis.extra import cli, ghostwriter
 from hypothesis.internal.compat import BaseExceptionGroup
 from hypothesis.strategies import builds, from_type, just, lists
+from hypothesis.strategies._internal.core import from_regex
 from hypothesis.strategies._internal.lazy import LazyStrategy
 
 varied_excepts = pytest.mark.parametrize("ex", [(), ValueError, (TypeError, re.error)])
@@ -431,6 +432,16 @@ def test_unrepr_identity_elem():
             lists(builds(Decimal)),
             {("decimal", "Decimal")},
         ),
+        # find the needed import for from_regex if needed
+        (
+            from_regex(re.compile(".+")),
+            {"re"},
+        ),
+        # but don't add superfluous imports
+        (
+            from_regex(".+"),
+            set(),
+        ),
     ],
 )
 def test_get_imports_for_strategy(strategy, imports):