-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
MAINT: Lazy import testing on python >=3.7 #14097
Conversation
ed484cf
to
7e32620
Compare
I can see removing
is a problem. What if we just import |
The problem seems two fold: first testing imports all of unittest, which is slow. Importing tester makes the testing namespace available, which many now expect. I think different python versions deal differently with implicit namespaces. I kinda wanted to see where the tests fail first. It may be possible to do something with the new module level getattr for modern python versions. In that case, we can lazy load tester for modern python. At this stage. This PR was more to get a full view of the problem as it stands now accross different python versions you all support. |
On newer python versions, we could use the module |
Could make the unittest import lazy, it is only needed for testing. Might also look if pytest can supply the needed functionality. EDIT: Note that we only keep the nose module for backwards compatibility, we don't use it and it is only tested if nose is present. |
Right. so I think I've isolated the 3 places where testing is used internally. Maybe the getattr trick @eric-wieser demo'ed could be used. Serious question, does |
I am not 100% sure, but I think |
Also, as soon as
In user code
|
The issue of using the
diff --git a/doc/sphinxext b/doc/sphinxext
--- a/doc/sphinxext
+++ b/doc/sphinxext
@@ -1 +1 @@
-Subproject commit a482f66913c1079d7439770f0119b55376bb1b81
+Subproject commit a482f66913c1079d7439770f0119b55376bb1b81-dirty
diff --git a/numpy/__init__.py b/numpy/__init__.py
index 14854f39f..54e856f15 100644
--- a/numpy/__init__.py
+++ b/numpy/__init__.py
@@ -183,12 +183,19 @@ else:
numarray = 'removed'
# Do we need this???
- # if sys.version_info[0] >= 3 and sys.version_info[1] >= 5:
+ if sys.version_info[0] >= 3 and sys.version_info[1] >= 7:
# pass
- # else:
+ def __getattr__(attr):
+ if attr == 'testing':
+ import importlib
+ import warnings
+ warnings.warn("some useful warning", stacklevel=2)
+ return importlib.import_module(".testing", __name__)
+ else:
# We don't actually use this ourselves anymore, but I'm not 100% sure that
# no-one else in the world is using it (though I hope not)
- # from .testing import Tester
+ from .testing import Tester
+
# Pytest testing
from numpy._pytesttester import PytestTester |
Honestly, I like the testing module, I just don't like the general performance hit on most code. |
I like the fact that it is readily available |
I have no idea what you mean by that. Isn't deprecation exactly what you just demoed, modulo the use of the wrong warning type? |
In your diff, you should also add |
You would have to create a whole new numpy testing package. Is that the plan too? I love the numpy testing functionality |
@hmaarrfk: I have no idea what you are talking about, the patch you give there looks correct (although |
@hmaarrfk if we deprecate it, all that would change is that you have to do from |
I think it's worth a recap here that def _(): # function to avoid `numpy` being in `globals()`
import numpy
return numpy.testing
testing = _() Ie, it does not mean |
Right, sorry. In any case, IIRC the deprecation discussion was about |
I guess I never use
I always thought it meant the exact same thing as
Though now I see that the first means
While the second means
|
Do you all want to impose the first syntax on everybody? |
Importing Tester is implicitly importing the testing namespace |
Don't most people using it as The other question is: what do we gain by deprecating vs leaving as a warning-free lazy import? |
I don't think we gain much. That said. You can likely use the getattr deprecation warnings to finally get rid of |
Is this the behaviour you want:
|
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 think I would prefer if this PR does not do any deprecations and limits to the speed up discussion. I also tend to think that there is not much of a point in deprecating testing
at all. The other changes (in the testing files) should not be necessary afterwards.
I assume that you will get all the speedups (on new enough python) that you were looking for and it is a nice, fairly short, PR.
numpy/__init__.py
Outdated
stacklevel=2) | ||
return 'removed' | ||
|
||
# This never gets triggered???? |
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.
Did you try what np.asdfasdjfkwer
gives you? (after import not during import)
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 did. I think I got it right, and I'll see if I can highlight a test that ensures that, or make a new one
numpy/__init__.py
Outdated
" Please use ``import numpy.testing as testing`` instead.", | ||
DeprecationWarning, | ||
stacklevel=2) | ||
return importlib.import_module("." + attr, __name__) |
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.
As Eric said, just use import, importlib is just harder to read.
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.
Yeah. I guess I just didn't understand the point of the PEP that had the importlib example
I'll remove all the deprecation stuff. There was just some discussion about it and I wanted to make things explicit. The get attr does what we need. |
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 think this might want a wider design for deprecating module level attributes before we do any deprecations. Perhaps a __deprecated_all__
member or something
numpy/__init__.py
Outdated
test = PytestTester(__name__) | ||
del PytestTester | ||
|
||
if sys.version_info[0] >= 3 and sys.version_info[1] >= 7: |
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.
sys.version_info[:2] >= (3, 7)
1a42f13
to
1079cf8
Compare
numpy/tests/test_public_api.py
Outdated
# on failure, this will produce alot of output, and potentially | ||
# deadlock the call without a call to `communicate` | ||
sub.communicate() | ||
sub.wait() |
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 not just subprocess.check_call(...)
, which handles all this for you?
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.
On failure, you would get some output that seems quite large, all looking like
File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
File "/home/mark/git/numpy/numpy/__init__.py", line 198, in __getattr__
from . import testing
File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
File "/home/mark/git/numpy/numpy/__init__.py", line 198, in __getattr__
from . import testing
File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
File "/home/mark/git/numpy/numpy/__init__.py", line 198, in __getattr__
from . import testing
File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
File "/home/mark/git/numpy/numpy/__init__.py", line 198, in __getattr__
from . import testing
File "<frozen importlib._bootstrap>", line 1032, in _handle_fromlist
RecursionError: maximum recursion depth exceeded
I think you end up hitting
Warning The preexec_fn parameter is not safe to use in the presence of threads in your application. The child process could deadlock before exec is called. If you must use it, keep it trivial! Minimize the number of libraries you call into.
I think the python 2.7 docs explain it better than the 3.7 docs, and suggests doing what I implemented above
https://docs.python.org/2/library/subprocess.html#subprocess.check_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.
output that seems quite large,
This output goes on the testing console, quite annoying. Therefore I opted to PIPE
the output. I guess I could also send it to a tempfile???
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.
If your goal is to discard it, check_call(stdout= subprocess.DEVNULL, stderr= subprocess.DEVNULL)
should do the job .
I'd maybe argue that the output contains useful debugging information, and pytest will discard it unless it fails anyway - so why not keep it around and use check_call
with no extra arguments?
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.
Pytest is not catching the error output and it is going straight to the consule when that one specific test is run
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.
hmm, ok, i found a 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.
Windows seems to have issues with piping stderr to stdout. Since this will only output stuff during a failure, I think it is acceptable to leave it undefined without complicating the code. Hopefully the test is more clear now.
f876581
to
8ae72f5
Compare
523e546
to
b669c28
Compare
@eric-wieser I think I've got this one to a place where I'm happy. Not sure if you are OK with the tests and the other changes I had to do to get things to pass. Feel free to remove the WIP tag |
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.
LGTM, modulo __dir__
question.
numpy/__init__.py
Outdated
|
||
|
||
def __dir__(): | ||
return __all__ + ['Tester'] |
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 should probably also add testing
? If there is a test for that somewhere for Tester
, can you add this too?
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.
In [3]: 'testing' in dir(numpy)
Out[3]: True
In [4]: numpy.__version__
Out[4]: '1.17.0'
you are right.
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.
AttributeError: module 'numpy.testing' has no attribute '__module__'
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 think I fixed up the tests. Hopefully that should address this concern.
0303f21
to
26e613b
Compare
26e613b
to
9ed23a3
Compare
seems like the failure isn't due to this PR right? |
Thanks Mark, LGTM. |
happy this made it in! |
Thank you all for the great discussion |
__all__.extend(['bool', 'int', 'float', 'complex', 'object', 'unicode', | ||
'str']) |
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 looks like a mistake to me - removed in #14881
See discussion here.
Just testing with CIs for now.
#14083