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

BUG: Defer to autodoc for signatures #221

Merged
merged 12 commits into from
Jun 27, 2020
Merged

BUG: Defer to autodoc for signatures #221

merged 12 commits into from
Jun 27, 2020

Conversation

thequackdaddy
Copy link
Contributor

@thequackdaddy thequackdaddy commented May 29, 2019

xref #220

I think this should pass. It fails because method signatures include self.

I'll be happy to help make this pass.

@thequackdaddy thequackdaddy changed the title TST: Desired test ENH: Allow user to say whether numpydoc or autodoc should create signatures. Jun 1, 2019
@jnothman
Copy link
Member

jnothman commented Jun 1, 2019

Why do you think this needs to be an option?

@thequackdaddy
Copy link
Contributor Author

thequackdaddy commented Jun 1, 2019

I don't. I just don't know if you want to defer this to autodoc. (More of an architecture question.)

Could just delete all that code in that case.

Throwing out ideas.

@thequackdaddy
Copy link
Contributor Author

I should have mentioned, it appears that the numpydoc way of getting signatures on class method is inconsistent between python 2 and python 3. Maybe another reason to just say, "screw it... use autodoc signatures".

But I don't want to upset the apple cart.

@thequackdaddy
Copy link
Contributor Author

(and who builds docs on python 2 anyway)

@bashtage
Copy link

bashtage commented Aug 1, 2019

I build latest statsmodels doc with this branch and it look good. Handles both function sigs in docstrings (e.g., from Cython) and removes self when needed. For example, (OLS)

image

@larsoner
Copy link
Collaborator

larsoner commented Aug 1, 2019

Maybe another reason to just say, "screw it... use autodoc signatures".

But I don't want to upset the apple cart.

Do tests pass if you always use autodoc? It would be nice to only have one code path if possible, especially if we know that the non-autodoc one is buggy.

@thequackdaddy
Copy link
Contributor Author

@larsoner I honestly haven't tried that. I'll try to get to it in the next few days...

@thequackdaddy
Copy link
Contributor Author

@larsoner So it was much easier than I thought...

Answer is that if I remove this code, we should revert to auto doc.

if not self['Signature'] and func is not None:
func, func_name = self.get_func()
try:
try:
signature = str(inspect.signature(func))
except (AttributeError, ValueError):
# try to read signature, backward compat for older Python
if sys.version_info[0] >= 3:
argspec = inspect.getfullargspec(func)
else:
argspec = inspect.getargspec(func)
signature = inspect.formatargspec(*argspec)
signature = '%s%s' % (func_name, signature.replace('*', r'\*'))
except TypeError:
signature = '%s()' % func_name
self['Signature'] = signature

The only induced failure is this one...

def test_escape_stars():
signature = str(doc3).split('\n')[0]
assert signature == r'my_signature(\*params, \*\*kwds)'
def my_func(a, b, **kwargs):
pass
fdoc = FunctionDoc(func=my_func)
assert fdoc['Signature'] == r'my_func(a, b, \*\*kwargs)'

pass

fdoc = FunctionDoc(func=my_func)
assert fdoc['Signature'] == r'my_func(a, b, \*\*kwargs)'
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might actually be important. Do things with *args, **kwargs render properly now? That's what this is meant to check/ensure. Can we replace this test rather than just remove it? Maybe check that the RST generated has the *s escaped properly?

Copy link
Collaborator

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

The other thing I wonder about is that git grep Signature has more hits in numpydoc than just the ones removed here. is self['Signature'] now created by autodoc automatically so we don't need to worry about this? Or is self['Signature'] always None after this PR so that logic elsewhere like the following could be simplified?

numpydoc/numpydoc.py:    sig = doc['Signature'] or getattr(obj, '__text_signature__', None)

@thequackdaddy
Copy link
Contributor Author

Would you be ok if I made a PR to turn on coveralls codecov? Might be easier to see what's really being tested and where.

@larsoner
Copy link
Collaborator

larsoner commented Aug 2, 2019

Sure, they're a bit buggy but we can configure then to be useful

@larsoner
Copy link
Collaborator

larsoner commented Aug 9, 2019

@thequackdaddy feel free to rebase now that #230 is in and then hopefully you can change the lines marked XXX here:

https://github.com/numpy/numpydoc/pull/230/files#diff-6624c1bee5f6adf586fc42bb69eb40b6R48-R54

@thequackdaddy
Copy link
Contributor Author

Rebase is done and I think that might be all we need. Thoughts?

Copy link
Collaborator

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

Looks good to me, +1 for merge. Would be good to get another set of eyes on it, though, in case I'm missing something about why we had that code in there in the first place. It is about 10 years old, though, so maybe its just there to work around something that has been fixed nowadays.

@jnothman
Copy link
Member

Please update the PR title. It certainly looks like a simple solution.

I'm building scikit-learn docs with this for review at scikit-learn/scikit-learn#14681

@thequackdaddy
Copy link
Contributor Author

@jnothman I changed the title.

@jnothman
Copy link
Member

I like how this looks in the member methods listing (autosummary). It also affects the signatures shown in method documentation. Here I see things like get_params(self, deep=True) become get_params(deep=True) which is fine, and __init__(self, /, *args, **kwargs) become __init__($self, /, *args, **kwargs) in some cases (I'm seeing it in sklearn.base, like here but not elsewhere like here). Any idea what this $ is about? More $ here

@thequackdaddy thequackdaddy changed the title ENH: Allow user to say whether numpydoc or autodoc should create signatures. BUG: Defer to autudoc for signatures Aug 23, 2019
@gforsyth
Copy link

@seberg -- this PR does fix the issues we were discussing at the dask developers meeting. If this can be merged and a new release cut then we can unpin numpydoc in a bunch of downstream projects that are affected by the double-escaping we were seeing in autosummary.

@jnothman
Copy link
Member

@gforsyth, am I alone in getting those $s rendering, per my comment above (#221 (comment))? I don't know why they are appearing...

@gforsyth
Copy link

Hey @jnothman -- I grepd over some rendered documentation to look for extra $s and I haven't come across any when building dask or distributed docs. I can try to build sklearn later today and see if I hit the same issue.

@thequackdaddy
Copy link
Contributor Author

Is there any chance of $module (probably not, mostly built-ins or lambdas) or $type here?

Easy to check for that as well and replace.

numpydoc/numpydoc.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

Just a minor simplification idea, otherwise LGTM

numpydoc/numpydoc.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

LGTM +1 for merge.

@bashtage or @gforsyth do you want to try it out and/or review? @jnothman you might also be interested in looking again

@thequackdaddy if nobody complains in a few days feel free to ping me again, I'll run some final tests locally and merge

Copy link

@gforsyth gforsyth left a comment

Choose a reason for hiding this comment

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

Changes look good and I've confirmed that building docs for dask/dask works as expected. Thanks for pushing on this!

Copy link
Member

@jnothman jnothman left a comment

Choose a reason for hiding this comment

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

Thanks

numpydoc/numpydoc.py Outdated Show resolved Hide resolved
numpydoc/numpydoc.py Outdated Show resolved Hide resolved
numpydoc/numpydoc.py Outdated Show resolved Hide resolved
numpydoc/tests/test_numpydoc.py Show resolved Hide resolved
assert (_clean_text_signature('func($self, foo="$self")')
== 'func(foo="$self")')
assert _clean_text_signature('func(self, other)') == 'func(self, other)'
assert _clean_text_signature('func(self, $self)') == 'func(self)'
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure this is a thing :)

sig = sig[end:-1]
params = sig.split(', ')
for param in ('$self', '$module', '$type', '/'):
if param in params:
Copy link
Member

Choose a reason for hiding this comment

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

I don't think it'll happen often, but I'm still not comfortable with the fact that this processes all parts of the sig. Can't we check explicitly that the / precedes a *, and that the $ is at the beginning, rather than use the split and join logic?

sig = re.sub(r'^\$(self|module|type), ', '', sig, count=1)
sig = re.sub(r', /, \*', ', *', sig, count=1)

Is this not sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll confess that I have (almost) no understanding of how these C text signature things work.

What you have would make it so func($self) would still be func($self). Is that alright? I have no idea what the possible syntax is for this.

If you (or someone else) has some kind of documentation on what is possible, that would make this back-and-forth much easier.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I meant to apply those substitutions after

    start_pattern = re.compile(r"^[^(]*\(")
    start, end = start_pattern.search(sig).span()
    start_sig = sig[start:end]
    sig = sig[end:-1]

They're just a bit more careful than splitting on every comma.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually I think this makes sense. Let me change that.

To handle func($self), I guess you could just add a ’, ‘ right before your code. Then use regex to strip off a trailing ’, ‘ if it exists.

Do you think that would work?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, didn't handle that case, did I?
Can use r'^\$(self|module|type)(, |$))?' which will strip a comma, or check that we're at the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Take a peek at this. I had to do something to check whether the / was the start of the string or preceded by a , .

Copy link
Member

@jnothman jnothman left a comment

Choose a reason for hiding this comment

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

The output looks good for Scikit-learn, and I think this is safer than it was yesterday, so I'm okay with it.

sig = sig[end:-1]
params = sig.split(', ')
for param in ('$self', '$module', '$type', '/'):
if param in params:
Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I meant to apply those substitutions after

    start_pattern = re.compile(r"^[^(]*\(")
    start, end = start_pattern.search(sig).span()
    start_sig = sig[start:end]
    sig = sig[end:-1]

They're just a bit more careful than splitting on every comma.

numpydoc/numpydoc.py Outdated Show resolved Hide resolved
@jnothman
Copy link
Member

So one last thing: I think this now stops displaying self, cls, etc. where it used to in method docs.

I suspect we should at least document this in a change log.

@thequackdaddy
Copy link
Contributor Author

So one last thing: I think this now stops displaying self, cls, etc. where it used to in method docs.

I suspect we should at least document this in a change log.

It looks like this project generally builds changelogs as part of the release cycle, unless I'm missing something? I just see a release notes file that looks like it was built for the 1.0 release. I don't see where I can edit in that?

@thequackdaddy
Copy link
Contributor Author

So one last thing: I think this now stops displaying self, cls, etc. where it used to in method docs.
I suspect we should at least document this in a change log.

It looks like this project generally builds changelogs as part of the release cycle, unless I'm missing something? I just see a release notes file that looks like it was built for the 1.0 release. I don't see where I can edit in that?

Never mind. I guess the file is meant to be cumulative. I'll give it the old college try.

@thequackdaddy thequackdaddy changed the title BUG: Defer to autudoc for signatures BUG: Defer to autodoc for signatures Jun 26, 2020
@thequackdaddy
Copy link
Contributor Author

Is everyone content?

@jnothman
Copy link
Member

Thank you @thequackdaddy!

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

Successfully merging this pull request may close these issues.

6 participants