Repository: HypothesisWorks/hypothesis

Issue:
Strategy functions defined by composite do not get picked up by Sphinx
@Zac-HD noticed this in #472 - a strategy defined using composite was not appearing in the documentation unless explicitly added to the list.

I *suspect* what's happening is that some attributes should be copied over to the generated function and are not. The most likely culprit is probably \_\_module\_\_. However I haven't looked into the code recently, so this might not be correct.


Discussion:
Nice timing :) I noticed this as well just this second and went looking for an issue.

I think the actual issue here is at least related to Hypothesis needing a release :)

Specifically, the latest release (3.6.1) still does: https://github.com/HypothesisWorks/hypothesis-python/blob/7ed4892866960154d5dffc40f32340e385434128/src/hypothesis/strategies.py#L901 whereas now it does https://github.com/HypothesisWorks/hypothesis-python/blame/master/src/hypothesis/strategies.py#L911 which at least is attempting to preserve `__doc__`. I'm gonna give it a shot for us on HEAD and see if that fixes things.
Hrm, nope, it doesn't, but it's definitely a step in the right direction, because before that, any composite strategy didn't have a `__doc__` to begin with.
Yeah, we ran into this problem in a pull request, so it's definitely still present in HEAD. You're right that it would definitely not have worked previously though!
Just checking up on this, I noticed that [the readthedocs page](https://hypothesis.readthedocs.io/en/latest/numpy.html) just lists `(*args, **kwargs)` for several functions.  These happen to be the ones decorated with `@defines_dtype_strategy`, but I can't reproduce the problem locally so the cause is unclear.  I've tried Sphinx 1.4.6 and 1.5.1, with and without Numpy installed.  Weird.

That aside, [the automodule docs](http://www.sphinx-doc.org/en/stable/ext/autodoc.html#directive-automodule) notes that everything is filtered on `__module__` to avoid documenting imported things.  Adding the `:imported-members:` tag *does* make composite strategies show up (along with, yes, everything imported) so I'm pretty sure this is the culprit.

So adding `accept.__module__ = f.__module__` to the [end of `composite`](https://github.com/HypothesisWorks/hypothesis-python/blob/f3d174b1143f03b7d5b91cf1e1704d3ccaaf58fa/src/hypothesis/strategies.py#L926) would be a sufficient solution, though I haven't checked for other effects.  I'd be happy to make a quick pull with this change and removing the explicit list I wrote out last time; on the other hand there may be somewhere more elegant to fix this (or better for the formatting goals).  Thoughts?
That seems like a perfectly reasonable solution to me and I'd be happy to accept a pull request implementing it, thanks. 👍 
RE the problem with \*args, \*\*kwargs, I suspect that's because functools.wraps is being insufficiently good at preserving the argspec. You might want to think about using the Hypothesis internal function `proxies` instead (sorry I should have caught this in review). https://github.com/HypothesisWorks/hypothesis-python/blob/master/src/hypothesis/internal/reflection.py#L454

Resolving patch:
diff --git a/src/hypothesis/extra/numpy.py b/src/hypothesis/extra/numpy.py
--- a/src/hypothesis/extra/numpy.py
+++ b/src/hypothesis/extra/numpy.py
@@ -17,8 +17,6 @@
 
 from __future__ import division, print_function, absolute_import
 
-import functools
-
 import numpy as np
 
 import hypothesis.strategies as st
@@ -26,6 +24,7 @@
 from hypothesis.errors import InvalidArgument
 from hypothesis.searchstrategy import SearchStrategy
 from hypothesis.internal.compat import hrange, text_type, binary_type
+from hypothesis.internal.reflection import proxies
 
 TIME_RESOLUTIONS = tuple('Y  M  D  h  m  s  ms  us  ns  ps  fs  as'.split())
 
@@ -193,7 +192,7 @@ def scalar_dtypes():
 
 def defines_dtype_strategy(strat):
     @st.defines_strategy
-    @functools.wraps(strat)
+    @proxies(strat)
     def inner(*args, **kwargs):
         return strat(*args, **kwargs).map(np.dtype)
     return inner
diff --git a/src/hypothesis/strategies.py b/src/hypothesis/strategies.py
--- a/src/hypothesis/strategies.py
+++ b/src/hypothesis/strategies.py
@@ -923,6 +923,7 @@ def draw(strategy):
 
                 return f(draw, *args, **kwargs)
         return CompositeStrategy()
+    accept.__module__ = f.__module__
     return accept
 
 


Test patch: