-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add PEP 519 support #6788
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
Add PEP 519 support #6788
Conversation
I'm not sure why this is failing on windows, suggestions? |
Looks like it's a permission error where it tries to write the files @JanSchulz Do you know anything about the permission model on appveyor |
Appveyor builds run as admin, so I don't think this is the problem. In my experience, such problem arise (on windows) from doing creation/deletions too fast: one process still has the file opened/locked and the next call is not allowed to do something with it. |
|
||
# Use NamedTemporaryFile: will be automatically deleted | ||
F = tempfile.NamedTemporaryFile(suffix='.' + extension) | ||
F.close() |
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 works on windows to get a named temp file name...
2a54821
to
1414c28
Compare
Updated and rebased on top of current master. I did have to add matplotlib.testing.nose and matplotlib.testing.nose.plugins to the included modules to build locally though, is that expected? |
Also, is it worth creating a separate pull request with @JanSchulz's closed_tempfile context manager? |
try: | ||
from pathlib import Path | ||
except ImportError: | ||
raise SkipTest("pathlib not installed") |
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.
@Kojoley What is the correct way to handle this now?
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.
It is OK to use raise SkipTest
as pytest supports it, but skip
(without raise
, just function call) (imported from from .testing import skip
) is preferable as it does not use nose when you are using pytest.
We are in the process of converting from nose to pytest (and the whole test suite will currently run under both) with almost all of the work being done by @Kojoley . Can you do the context manager change in this PR? |
2e04700
to
39cdd81
Compare
39cdd81
to
76bb608
Compare
This should be all good now. |
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.
There is a lot of code duplication in the tests. I think they need to be refactored to have less code duplication.
@@ -2701,3 +2705,44 @@ def __exit__(self, exc_type, exc_value, traceback): | |||
os.rmdir(path) | |||
except OSError: | |||
pass | |||
|
|||
|
|||
try: |
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 we should consider making cbook
a python module with different submodule, and have all the backward compatibility fixes in a file where we cleary can keep track of it.
That would require more work (creating the cbook
folder, __init__.py
, a fixes.py
module with those, updating the setup.py
), but would allow us to split this huge file into more manageable size files.
What do you think?
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.
Sure, I can do that.
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.
So I'm not sure what can actually be moved from matplotlib.cbook to matplotlib.cbook.fixes, as the fspath stuff is the only compatibility things I can see, and there's already matplotlib.compat...
""" | ||
with tempfile.NamedTemporaryFile( | ||
'w+t', suffix=suffix, delete=False | ||
) as test_file: |
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.
That parenthesis here is weird. I am surprised pep8 allows this. Is there anyway to break the line in another 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.
with tempfile.NamedTemporaryFile('w+t', suffix=suffix,
delete=False) as test_file:
fits within 79 chars, and looks reasonable, I think.
@contextmanager | ||
def closed_tempfile(suffix='', text=None): | ||
""" | ||
Context manager which yields the path to a closed temporary file with the |
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.
Can you please rewrite this docstring with the numpydoc format? I find the docstring relatively unclear on what exactly the function does. If I understand correctly, it creates, closes and returns a path to a file and deletes it on exiting the context. It is also unclear where the file is created. A note on how it relates and differs from tempfile.NamedTemporaryFile
would help clarify what the function does.
Also, this is publicly available - do we want this publicly available? (if not, then the docstring comment can be ignored.)
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.
Does publicly available mean should be used outside of matplotlib, or is the recommended option for other parts of matplotlib? I don't see much use outside matplotlib, unless you were already using matplotlib's testing utils.
|
||
if not animation.writers.is_available(writer): | ||
skip("writer '%s' not available on this system" | ||
% writer) |
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 am, again, surprised pep8 allows that.
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.
It doesn't for me; I don't know why this is passing either.
raise TypeError("expected str, bytes or os.PathLike object, not " | ||
+ path_type.__name__) | ||
|
||
def fspath_no_except(path): |
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 should be a private function.
If it is not a private function, it should be documented.
I also don't really like this name, but I am not coming up with anything better for now…
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 seem to recall something (Sphinx? NumPyDoc?) using a safe_
prefix; so something like safe_fspath
(or even more explicitly safe_fspath_to_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.
That'd be a much better 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.
I'm not a fan of safe_fspath
, as it's not really safe, since the reason it exists to to pass through anything that's not a PEP 519 path, and validating anything else doesn't happen. Also, there's no guarantee that it'll return a string, as __fspath__
can return either bytes or a string (I've special-cased pathlib
here as there exist versions of pathlib
without PEP 519 support), so safe_fspath_to_str
is even worse. How about fspath_passthrough
?
return six.text_type(path) | ||
|
||
raise TypeError("expected str, bytes or os.PathLike object, not " | ||
+ path_type.__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.
That is not pep8 compliant. Please break the line after the +.
I would also prefer using string formatting instead of concatenation.
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.
PEP8 does not prescribe either way, but has a slight preference for the code as it's written.
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.
So change or leave?
|
||
|
||
@cleanup | ||
def check_save_animation_pep_519(writer, extension='mp4'): |
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 would avoid referencing to peps in function name. It is unclear what it does.
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.
What about check_save_animation_PathLike?
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.
check_save_animation_path_like
?
|
||
|
||
@cleanup | ||
def check_save_animation_pathlib(writer, extension='mp4'): |
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 test and the previous one looks like they could be refactoring to loop other Path and fspath compatible object to minimize the code duplication.
|
||
|
||
@cleanup | ||
def test_pdfpages_accept_pep_519(): |
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.
Those two tests look like they could be refactored into one.
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.
The test_pdfpages_accept_pep_519 and test_savefig_accept_pathlib? I'm not sure it's possible to skip part of a test and have that appear in nose/pytest.
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.
mmh… good question.
@Kojoley any idea on that?
text = "This is a test" | ||
with closed_tempfile(".txt", text=text) as f: | ||
with open(f, "rt") as g: | ||
assert text == g.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.
Can you add the approriate code to be able to run the tests in this file independantly from the rest?
if __name__=='__main__':
nose.runmodule(argv=['-s','--with-doctest'], exit=False)
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'll add this
This patch will be useful for the future! Thanks for tackling this work. |
I see that this is tagged with the "2.1 (next point release)" milestone -- and I don't follow matplotlib development very closely, so I'm not sure when to expect 2.0 to be released. Is there any chance of this (or a stripped-down version of this) functionality being included in a 1.5.x release relatively soon? I assume not, but I don't see too much harm in asking. I started using Python 3.6 for my analysis code a week or so ago; it's been a while since the last beta, rc1 is due tonight (as I understand it), and the final release is scheduled for 2016-12-16. PEP 519 is the main thing motivating me to install 3.6 on my machines, since I use More generally, is there a good justification for the current behavior of throwing an error from matplotlib if e.g. |
There will be no more 1.5.x releases, 2.0 has an rc1 out final hopefully in late December / early January . 2.1 should be something like late Q1/early Q2 2017. |
@@ -1173,7 +1176,7 @@ def listFiles(root, patterns='*', recurse=1, return_folders=0): | |||
pattern_list = patterns.split(';') | |||
results = [] | |||
|
|||
for dirname, dirs, files in os.walk(root): | |||
for dirname, dirs, files in os.walk(fspath_no_except(root)): |
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.
Shouldn't the standard library be able to handle this on it's own?
Is this likely to land soon? |
Closes #5968 and replaces #6772. This should allow pathlib.Path or other PEP 519 objects to be used with matplotlib.