10000 ENH: switch to 'with' statement for open/close file in lib.npyio._savez by rherault-pro · Pull Request #6545 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: switch to 'with' statement for open/close file in lib.npyio._savez #6545

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

Closed
wants to merge 1 commit into from

Conversation

rherault-pro
Copy link

The function _savez stages data during archiving through temporary
files. Before that commit these temp and zip files were handled through
old open/close style. This commits switches to 'with' statement.
which is the recommendation since python 2.5. It's simplifying A LOT
exception handling and garbage collection.

Due to uncompatibility between zipfile in python2.6 and with statement,
ZipFile class has been subclassed to add needed stub methods.

fd, tmpfile = tempfile.mkstemp(suffix='-numpy.npy')
os.close(fd)
try:
with zipfile_factory(file, mode="w", compression=compression) as zipf:
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 your context handler is exception safe, so either needs a fix or this should still be in a try..except clause.

Copy link
Member

Choose a reason for hiding this comment

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

OK, exception handler will reraise.

Copy link
Author

Choose a reason for hiding this comment

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

As the exception handler was only here to remove/close files in the original version, no need to add try/except mechanism to the with statement.
This this exactly the case given in the "Motivation and summary" section of pep-0343 .

@rherault-pro
Copy link
Author

I've got a problem with test_load_refcount that randomly fails even on the master branch
#6571

So sometimes Travis check fails or not with the same exact code.

@@ -599,13 +611,41 @@ def savez_compressed(file, *args, **kwds):
"""
_savez(file, args, kwds, True)

@contextlib.contextmanager
Copy link
Member

Choose a reason for hiding this comment

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

Maybe something simpler?

@contextmanager
def TempFileName(suffix='', prefix='tmp', dir=None):
    fid, name = mkstemp(suffix, prefix, dir)
    os.close(fid)
    yield name
    os.remove(name)

This is a common pattern in a lot of tests, e.g, in test_io.py

Copy link
Member

Choose a reason for hiding this comment

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

Nb that that's not the right way to write a context manager -- if the with
block raises an exception then the yield will raise that exception and
os.remove won't be called, you still need a try/finally.
On Nov 5, 2015 2:03 PM, "Charles Harris" notifications@github.com wrote:

In numpy/lib/npyio.py
#6545 (comment):

@@ -599,13 +611,41 @@ def savez_compressed(file, _args, *_kwds):
"""
_savez(file, args, kwds, True)

+@contextlib.contextmanager

Maybe something simpler?

@contextmanager
def TempFileName(suffix='', prefix='tmp', dir=None):
fid, name = mkstemp(suffix, prefix, dir)
os.close(fid)
yield name
os.remove(name)

This is a common pattern in a lot of tests, e.g, in test_io.py


Reply to this email directly or view it on GitHub
https://github.com/numpy/numpy/pull/6545/files#r44076176.

Copy link
Member

Choose a reason for hiding this comment

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

OK, same thing rewritten as an official context manager ;)

Copy link
Member

Choose a reason for hiding this comment

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

@njsmith I believe that for real exception handlers __exit__ is called, then the exception is reraised.

Copy link
Contributor

Choose a reason for hiding this comment

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

we also often just use a temporary directory for this situation, a context manager for that is pretty simple (and also already exists in python3.x) we really should have it in our test utils

Copy link
Contributor

Choose a reason for hiding this comment

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

oh we already have one but its not correct ...

Copy link
Member

Choose a reason for hiding this comment

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

@juliantaylor See also the discussion at #6660.

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this also a reasonable place to use contextlib.closing, instead of creating your own class? This seems like exactly the situation that function / decorator exists...

(PS sorry for the double comment, the last one was in the wrong place)

Copy link
Author

Choose a reason for hiding this comment

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

@wackywendell not here but for the zipfile, yes

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, sorry, I don't know why I'm having so much trouble putting my comments on the right line.

with tempfile.NamedTemporaryFile(*args, **local_kwds) as fid:
filename = fid.name
yield fid
try:
Copy link
Contributor

Choose a reason for hiding this comment

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

hm where is the os.close?

Copy link
Author

Choose a reason for hiding this comment

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

The os.close is automatically performed by the context manager of NamedTemporaryFile at the exit of the with statement (here before line 633).

The whole pain came from the fact that NamedTemporaryFile is a factory function not a class, so you can not subclass it.

Copy link
Member

Choose a reason for hiding this comment

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

What happens when an error is raised from yield?

Copy link
Author

Choose a reason for hiding this comment

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

If you mean by the caller of DeleteOnContextExitNamedTemporaryFile before the return of "yield fid" then it calls the exit method of fid. That is the purpose of using the context manager NamedTemporaryFile. See
https://www.python.org/dev/peps/pep-0343

The exit method will close but not delete the file.
After that our context manager takes the relay and deletes the file.

Copy link
Member

Choose a reason for hiding this comment

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

Exit method of NamedTemporaryFile, which closes the file but doesn't delete it. The __exit__ method will then reraise the error and you will not catch it.

Copy link
Author

Choose a reason for hiding this comment

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

OK, I've described the good way of doing it by rewritting class _TemporaryFileWrapper from tempfile.py on my last comment on #6866

Copy link
Member

Choose a reason for hiding this comment

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

I think you'd want ExitStack here to implement this properly

@rherault-pro rherault-pro force-pushed the withstatementinsavez 341A branch 8 times, most recently from f9a31c2 to 0583fd8 Compare November 24, 2015 20:48
@charris
Copy link
Member
charris commented Dec 20, 2015

I've committed a temppath context manager in #6866. If you look there, I also refactor a test to use it. You can get at it (and tempdir) by rebasing on master and importing from numpy.testing. tempdir may be easier to use here.

@rherault-pro
Copy link
Author
rherault-pro F438 commented Dec 20, 2015

Thank you, but as I have replied on #6866, the next step is to replace in-disk tempfile by possibly in-mem tempfile (at user choice) for npz saving; returning a file path prevents that.
Moreover, that context manager is in the test part of the source, it may not be cleaned to use it here in core part of the source.

@charris
Copy link
Member
charris commented Dec 20, 2015

it may not be cleaned to use it here in core part of the source.
? it is distributed with the rest. Other projects import from numpy.testing.

Note that we are dropping Python 2.6 after 1.11 is branched, so it might make sense to postpone this until then so that the 2.6 code can be removed.

@rherault-pro
Copy link
Author

Yes of course less compat code better it is

@homu
Copy link
Contributor
homu commented Feb 9, 2016

☔ The latest upstream changes (presumably #7133) made this pull request unmergeable. Please resolve the merge conflicts.

@rherault-pro rherault-pro force-pushed the withstatementinsavez branch from 4a18bf7 to c1aa2d8 Compare March 20, 2016 17:23
The function _savez stages data during archiving through temporary
files. Before that commit these temp and zip files were handled through
old open/close style. This commits switches to 'with' statement.
which is the recommendation since python 2.5. It's simplifying A LOT
exception handling and garbage collection.

Due to uncompatibility between zipfile in python2.6 and with statement,
ZipFile class has been subclassed to add needed stub methods.

BUG: WinNT prevents a file to be opened twice

Using contextlib.closing rather than own class
@rherault-pro rherault-pro force-pushed the withstatementinsavez branch from c1aa2d8 to 7d0bca1 Compare March 20, 2016 21:11
@charris charris mentioned this pull request Nov 5, 2017
@charris
Copy link
Member
charris commented Nov 5, 2017

See also #9510.

@charris
Copy link
Member
charris commented Nov 5, 2017

OK, going to close this. Python 3.6 allows 8A15 writing directly to the zipfile with no temporary file needed, so I think that problem will fix itself. For lesser versions of Python we can live with it.

The temppath context manager should probably be made more available.

@rherault-insa Thanks for the PR, even if it didn't get in it led to some useful discussion.

@charris charris closed this Nov 5, 2017
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.

7 participants
0