Repository: HypothesisWorks/hypothesis

Issue:
#3540 causes `RecursionError` in particular draw pattern  
#3540 introduces a change that causes a `RecursionError` in [MyGrad's test suite](https://github.com/rsokl/MyGrad/actions/runs/4002289112/jobs/6869371400).

This is a weird bug. It seems to be specific to drawing from an array-based strategy that spans multiple lines *and* that includes an identity mapping (I said it was weird!).

The following tests pass (note the xfail)

```python
import pytest

import hypothesis.extra.numpy as hnp
import hypothesis.strategies as st
from hypothesis import given

@given(st.data())
def test_one_line_identity_passes(data):
    x = data.draw(hnp.arrays(shape=(2,), dtype=float).map(lambda x: x))


@pytest.mark.xfail(raises=RecursionError, strict=True)
@given(st.data())
def test_multiline_identity_fails(data):
    x = data.draw(
        hnp.arrays(
            shape=(2,),
            dtype=float,
        ).map(lambda x: x)
    )


@given(st.data())
def test_multiline_non_identity_passes(data):
    x = data.draw(
        hnp.arrays(
            shape=(2,),
            dtype=float,
        ).map(lambda x: 1 * x)
    )
    
    
@given(st.data())
def test_multiline_non_array_passes(data):
    x = data.draw(
        st.lists(
            st.integers(
                1,
                5,
            )
        ).map(lambda x: x)
    )
```


Discussion:
Wow, finally sniped this and feel kinda stupid: the following call can recurse, but *doesn't copy the stack used for cycle-detection.*

https://github.com/HypothesisWorks/hypothesis/blob/48165d90f6afc1c0cd618ae0659f02a88b579f8b/hypothesis-python/src/hypothesis/vendor/pretty.py#L359-L361

Bleeding obvious once you see it, and it's a single-line fix🤦‍♂️ 

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/vendor/pretty.py b/hypothesis-python/src/hypothesis/vendor/pretty.py
--- a/hypothesis-python/src/hypothesis/vendor/pretty.py
+++ b/hypothesis-python/src/hypothesis/vendor/pretty.py
@@ -357,6 +357,7 @@ def repr_call(self, func_name, args, kwargs, *, force_split=None):
             # We're OK with printing this call on a single line, but will it fit?
             # If not, we'd rather fall back to one-argument-per-line instead.
             p = RepresentationPrinter()
+            p.stack = self.stack.copy()
             p.known_object_printers = self.known_object_printers
             p.repr_call("_" * self.output_width, args, kwargs, force_split=False)
             s = p.getvalue()


Test patch:
diff --git a/hypothesis-python/tests/cover/test_slippage.py b/hypothesis-python/tests/cover/test_slippage.py
--- a/hypothesis-python/tests/cover/test_slippage.py
+++ b/hypothesis-python/tests/cover/test_slippage.py
@@ -55,7 +55,7 @@ def test(i):
 
 
 def test_shows_target_scores_with_multiple_failures():
-    @settings(database=None, max_examples=100)
+    @settings(derandomize=True, max_examples=10_000)
     @given(st.integers())
     def test(i):
         target(i)
diff --git a/hypothesis-python/tests/numpy/test_gen_data.py b/hypothesis-python/tests/numpy/test_gen_data.py
--- a/hypothesis-python/tests/numpy/test_gen_data.py
+++ b/hypothesis-python/tests/numpy/test_gen_data.py
@@ -1221,3 +1221,14 @@ def test_basic_indices_generate_valid_indexers(
 def test_array_owns_memory(x: np.ndarray):
     assert x.base is None
     assert x[...].base is x
+
+
+@given(st.data())
+def test_no_recursion_in_multi_line_reprs_issue_3560(data):
+    data.draw(nps.arrays(shape=(2,), dtype=float).map(lambda x: x))
+    data.draw(
+        nps.arrays(
+            shape=(2,),
+            dtype=float,
+        ).map(lambda x: x)
+    )