-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
DOC: make some doctests in user,reference pass pytest #20288
Conversation
Thanks for working on this @pdebuyl! Doing:
is quite noisy, as well as fragile (easy to forget in the future). Why can't this import be automatically added by the refguide checker? I'd hope
This is also something that should be done by the doctest runner script/framework. It's just visual noise otherwise, and easy to forget in new tests. And adding explicit seeds in order to make doctests pass in the example code itself is not a good alternative.
Can you explain what this means? It sounds like that's way out of scope for doctests(/examples). |
The whitespace and repr changes here all look good to me. |
Hi @rgommers thanks for giving it a look :-)
The program
Some tests do not set the seed currently, so that there would be many edits for random results. But only "once more" ideally :-)
The doctests that rely on C or Cython extensions modules fail because the modules are not build. |
PS: forgot to mention. pytest can add the numpy import automatically via a conftest.py file, so that this part could be removed. I would add a file |
I think it'd be good to repeat what has been our stance on this for a very long time: doctests are not tests. Running them as tests with plain In general, the exact string representation of outputs is also going to vary over platforms, compiler and Python versions, etc. So getting repr's to work outside of one fixed config is not possible. What you then end up doing is skipping such tests, which is worse than ensuring they work correctly on a single well-defined CI config. So having |
This would be nice to achieve. I'm not sure it's possible in general, but given the right "harness" it may be possible to do this in CI (with a list of files to skip probably because they're too complex). |
Thanks for providing more information. I have been somehow misguided apparently. Does it make any sense to work on pytest for the doctest? The goal is not that far off. Using global directives (i.e. the option There are tradeoffs to make as well. For instance, some platform specific doctypes cannot pass CI runs on all platforms. The matplotlib repr look like Being a side contributor I am fine with any direction that NumPy takes. Is the plan to keep on using the |
It is impossible by "just" running |
I'm sorry about that. Don't get me wrong, most of this PR is still quite useful - thank you for working on it!
The reason some folks wanted to get rid of The one thing to avoid is to treat the examples like unit tests - they're examples, so need to teach best practices and not be cluttered. The one reason to run them through
These Matplotlib repr's are probably best left out - they don't really serve a purpose except distract the reader from the example code.
Yes, I suspected that. It may not be worth the trouble. Such tests are going to be inherently fragile and slow to run. |
No worry about the confusion! I'll add a conftest.py and clean up the imports. Regarding
apart from writing a pytest plugin to allow variation in the object's adress I don't see a way forward. refguide_check.py does a lot of "progressive" skipping of the output. I'll check if an existing plugin would work. |
I'm not sure what that means. If it makes sense to show >>> ax = plt.figure()
>>> ax.plot(...)
>>> ax.plot(....)
>>> ax.legend should not be polluted by |
In your example only the last line would show the output.
or
for ellipsis matching. |
Okay, bad example then - if we change it with any other MPL calls, I'd prefer not to show unused return values. And the legend output also not. It's be great if |
I removed the explicit imports. To get an idea of the remaining annoyances:
doctestplus was not actually necessary at this stage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pdebuyl, this is starting to get close. A few inline comments, and one concern for maintenance: doc/pytest.ini
is a copy of pytest.ini
, that seems to break the DRY principle. Is there a way to get around that? Seems like that should be possible via a test script. I also don't quite understand why it's needed in the first place, the pytest.ini
in the root of the repo should be picked up I'd think.
@@ -1685,6 +1687,7 @@ With Matplotlib, you have access to an enormous number of visualization options. | |||
>>> Z = np.sin(R) | |||
|
|||
>>> ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis') | |||
<mpl_toolkits.mplot3d.art3d.Poly3DCollection object at 0x...> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I was talking about. This is 4 lines of random output that just detracts from the example. We seem to have a couple more, but not many luckily:
$ rg "matplotlib.lines"
doc/source/reference/routines.polynomials.classes.rst
338: [<matplotlib.lines.Line2D object at 0x2136c10>]
341: [<matplotlib.lines.Line2D object at 0x1cf2890>]
numpy/core/function_base.py
112: [<matplotlib.lines.Line2D object at 0x...>]
114: [<matplotlib.lines.Line2D object at 0x...>]
267: [<matplotlib.lines.Line2D object at 0x...>]
269: [<matplotlib.lines.Line2D object at 0x...>]
380: [<matplotlib.lines.Line2D object at 0x...>]
382: [<matplotlib.lines.Line2D object at 0x...>]
numpy/lib/function_base.py
1514: [<matplotlib.lines.Line2D object at 0x...>]
1516: [<matplotlib.lines.Line2D object at 0x...>]
2904: [<matplotlib.lines.Line2D object at 0x...>]
2923: [<matplotlib.lines.Line2D object at 0x...>]
3013: [<matplotlib.lines.Line2D object at 0x...>]
3032: [<matplotlib.lines.Line2D object at 0x...>]
3116: [<matplotlib.lines.Line2D object at 0x...>]
3135: [<matplotlib.lines.Line2D object at 0x...>]
3218: [<matplotlib.lines.Line2D object at 0x...>]
3235: [<matplotlib.lines.Line2D object at 0x...>]
3497: [<matplotlib.lines.Line2D object at 0x...>]
3514: [<matplotlib.lines.Line2D object at 0x...>]
3603: [<matplotlib.lines.Line2D object at 0x...>]
numpy/fft/_pocketfft.py
207: [<matplotlib.lines.Line2D object at 0x...>, <matplotlib.lines.Line2D object at 0x...>]
304: [<matplotlib.lines.Line2D object at ...>]
306: [<matplotlib.lines.Line2D object at ...>]
I'd much prefer not to see these lines. It's best just not to see them (the example runner should ignore them), second best to add _ =
to the plot calls to swallow the output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For NumPy we don't have many plots in the docs, so it's not a big deal. In SciPy this would be a lot of noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This output line can still be removed.
148293216 # may vary | ||
>>> f(a) | ||
>>> f(a) #doctest: +SKIP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need #doctest: +SKIP
if we already have # may vary
for the output? This should work without the skip.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is linked to the fact that I was working toward a split refguide_check.py / pytest testing scenario. The # may vary
is internal refguide_check.py
cooking, pytest does not take it into consideration.
Hi @rgommers I suppressed the duplication, one pytest.ini is indeed sufficient. Regarding matplotlib output, solutions:
|
I like the bonus:) Just to make sure I understand: you want to keep the output line with ellipsis in this case? Why can't the doctest plugin just swallow the output of any plot call? I.e. the same as dynamically adding |
"Solution 3" allows to keep "full matplotlib output lines" such as |
That's even worse than the ellipsis variant I think.
This would be good. |
This way? |
Yes, that looks great, thanks. |
Cool, let me know if I should do anything else to get the PR in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, only a couple of small things left.
I can live with the duplicated conftest.py
. Everything else seems like an improvement to me.
@@ -1685,6 +1687,7 @@ With Matplotlib, you have access to an enormous number of visualization options. | |||
>>> Z = np.sin(R) | |||
|
|||
>>> ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis') | |||
<mpl_toolkits.mplot3d.art3d.Poly3DCollection object at 0x...> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This output line can still be removed.
@@ -57,7 +57,7 @@ array([[2., 0., 0., 0., 0.], | |||
Notice that the return type is a standard ``numpy.ndarray``. | |||
|
|||
>>> type(np.multiply(arr, 2)) | |||
numpy.ndarray | |||
<class 'numpy.ndarray'> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note (change is fine): this is Python interpreter, the old value was IPython interpreter. Most users will see the latter, so this is not necessarily helpful, but I don't mind much either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize this. Any version is fine by me.
The duplicate conftest make sense in a way, as we don't test the NumPy library and its doc in the same way. Regarding the output of random functions, Passing other parts of the docs may be more challenging and maybe less relevant (the |
Hi @rgommers I think that I addressed all the comments :-) |
This looks good to me now, thanks @pdebuyl. There are a couple of CI errors that need addressing, most importantly the doc build should pass. Could you have a look at that? |
Hi @rgommers the CI build break on the docs at an unrelated location The error is I fixed a linter error in the meantime. |
A rebase on current main will probably fix the circleci error. |
1. Add `import numpy as np` in rst files 2. Update NumPy repr for array (whitespace) 3. Update bytearray representation 4. Fix tiny output formatting (`<class ...>`, etc) 5. Format tracebacks 6. Skip random number tests or some platform-dependent outputs 7. Add `<matplotlib. ... at 0x...>` or similar output lines where missing 8. Set seed
Add conftest.py and pytest.ini files in doc directory
Contains code inspired by https://github.com/wooyek/pytest-doctest-ellipsis-markers (MIT license)
731f0d0
to
831849e
Compare
Thanks @charris rebase done (+ force push) |
"All checks have passed" :-) |
I fixed the merge conflict that came up. |
Thanks. Any follow-on niggles can be handled in future PRs. |
Great:) Thanks for all the work on this @pdebuyl! |
🥳 |
import numpy as np
in rst files<class ...>
, etc)<matplotlib. ... at 0x...>
or similar output lines wheremissing
Status:
pytest --doctest-glob="*.rst" doc/source/user
still fails 4 tests related to example compiled extensions.doc/source/user/basics.rec.rst
fails because of the output ofstructured_to_unstructured
on line 490. I don't know what is expected there.pytest --doctest-glob="*.rst" doc/source/reference
only fails the cython file.See #15846 for previous discussion.
This PR is also related to #15845 about using astropy/pytest-doctestplus. This PR brings many more
.rst
files to pass pytest, which is (in my opinion) good.I had to add a few
+SKIP
directives for random results. Those could be removed if we have a "norm" for random numbers in the docs, i.e. setting seeds and expecting the random output to be set.Another further progress would be related to automatically building the compiled extensions.