Repository navigation
Remove ResourceWarnings when fits.open is given a non-FITS file (fix #6168) - #6177
Conversation
| # Close the file: see | ||
| # https://github.com/astropy/astropy/issues/6168 | ||
| # | ||
| # TODO: is this a sensible place to close things? |
There was a problem hiding this comment.
I would remove these question like todos from the code, and discuss it in the PR itself
| f.write('0.1 0.2\n') | ||
|
|
||
| # This should raise an IOError because there is no end card. | ||
| with catch_warnings() as ws: |
There was a problem hiding this comment.
That seems a bit fragile without providing the warning-type explicitly. I don't know where RessourceWarning is defined but we should definetly only catch these here.
There was a problem hiding this comment.
It's a very good question as to why I didn't restrict catch_warnings to just ResourceWarning. I blame it on exploratory code. When I get time I'll fix this.
There was a problem hiding this comment.
It would be good to check that the test fails without your fix: ResourceWarning is ignored by default (https://docs.python.org/3/library/warnings.html#default-warning-filters), not sure what's the status with pytest's warning plugin.
There was a problem hiding this comment.
Also I don't think you can tell which warnings you want to catch with catch_warnings(). If needed the list of caught warnings can be filtered, but here the goal is to have no warning so the test seems fine.
There was a problem hiding this comment.
@saimn What you mean with "I don't think you can tell which warnings you want to catch", it should be possible according to the docs: http://docs.astropy.org/en/stable/api/astropy.tests.helper.catch_warnings.html#catch-warnings
There was a problem hiding this comment.
@MSeifert04 - Oh sorry, I thought it was using warnings.catch_warnings, I didn't know (or remember) the Astropy wrapper.
|
|
||
| # If we're trying to read only and no header units were found, | ||
| # raise and exception | ||
| # raise an exception |
|
I left some comments but I don't know if that's the correct place either. I'm also not sure if I have time to look into this the next days. |
|
Thinking about this, I'm surprised there is no other way currently to check if a file is a FITS one or not. There is actually some code to identify a FITS file and it's used for the So about where to insert the |
|
The reason why I put it in |
|
Yeah I understand your approach, and have mixed feelings on this. I agree the file should also be closed if it fails on the next HDUs, though it's unlikely to happen. |
Something like this was suggested in the PR review - see astropy#6177 (comment) - and it has uncovered an apparent flaw in my fix, as the new test that was added to check this fails (it passes if the changes to hdulist.py are reverted).
|
@saimn I tried to add a check to not close the file if it's actually a file-like object, but the test I added to check this fails. As this new test passes without my changes to
I didn't try moving the location of the close as I didn't want to change too many things at once. |
|
@DougBurke - My bad, |
|
@saimn - well, I think the overall point you brought up - that file objects shouldn't be closed - is a good one, so it's not all bad ;-) |
| except IOError: | ||
| # Close the file: see | ||
| # https://github.com/astropy/astropy/issues/6168 | ||
| fileobj.close() |
There was a problem hiding this comment.
Use self._file.close() instead (it takes care of the memap).
|
@DougBurke - Right ;). So about closing the file only if a filename was passed, I think you need to add a flag in |
|
@saimn - here's my initial attempt at adding in a flag as you suggested. I'll include my commit message for 2c45952 as it's not clear to me whether my bodge is sufficient:
|
|
Could you rebase on master to make sure this works with #6082 ? |
|
Rebasing. The original tip was 2c45952 The reason for not closing the bzip/gzip handlers was mentioned in the commit message and comment at #6177 (comment) (well, it's hardly a reason why, more saying I wasn't sure if it was a sensible thing to do, since I don't want to create new warnings due to closing stuff that's going to close anyway). I'd feel safer having tests to check these cases, but don't know if I have the time or knowledge to create them. On my machine the test |
2c45952 to
07ecc34
Compare
Something like this was suggested in the PR review - see astropy#6177 (comment) - and it has uncovered an apparent flaw in my fix, as the new test that was added to check this fails (it passes if the changes to hdulist.py are reverted).
|
Oh sorry, it seems I did not read carefully your commit message 😕 . So now I understand why, but I think these objects should also be closed (though I agree it's difficult to do without testing). |
|
So, the feature freeze for v2.0 is very close, and given my timezone it's difficult for me to help further. I think it's almost ready, not too risky because it's just closing the file in case of an exception. But it would be great to have another opinion, @pllim @MSeifert04 ? |
|
Well, I'm only available to work on this early morning or late night here, so my timezone is shot! Thanks for the input @saimn |
|
I've added an entry to |
|
Thanks @DougBurke, I tested with gzip and bz2 but they do not raise the Otherwise this looks good so no objection from me to get this in 2.0. Also, if you can squash the commit it's great. |
|
@DougBurke - Yes, looks good ! Just left 2 minor comments, otherwise I think we can merge once the tests pass. |
|
@DougBurke - There is no strict policy about squashing commits, but here, given the small number of changed lines it would make the history cleaner (it's appreciable when you use |
|
Hold your horses, I am reviewing... |
pllim
left a comment
There was a problem hiding this comment.
I need some answers before I am comfortable with this being merged. Thanks for your patience!
| - Use more sensible fix values for invalid NAXISj header values. [#5935] | ||
|
|
||
| - Close file on error to avoid creating a ``ResourceWarning`` warning | ||
| about an unclosed file. [#6168] |
There was a problem hiding this comment.
You also need to include this PR's number: [#6168, #6177]
|
|
||
| # Avoid a spurious pyflakes warning about an unused import | ||
| # of six by adding a reason string | ||
| @pytest.mark.skipif(six.PY2, reason='six.PY2') |
There was a problem hiding this comment.
To avoid pyflakes warning, you could have just write from ....extern import six # noqa above. The reason here should be used to explain why the test is skipped for Python 2 (although I know you explained it in the docstring).
But I am okay with leaving this as-is because Python 2 support will be removed in about 3 months anyway.
| # Avoid a spurious pyflakes warning about an unused import | ||
| # of six by adding a reason string | ||
| @pytest.mark.skipif(six.PY2, reason='six.PY2') | ||
| @pytest.mark.parametrize('ignore', [False, True]) |
There was a problem hiding this comment.
Why does this need to be two different parametrize decorators? Just one would be sufficient as it can support multiple input arguments at once.
There was a problem hiding this comment.
To me it's clearer that it's iterating through both the ignore and fileobj arguments (i.e. you get 2 * 2 combinations of tests). If I write
@pytest.mark.parametrize('ignore,fileobj', [(False, False), (False, True), (True, False), (True,True)])
it is harder for me to check that I have all combinations.
| open a non-FITS file when using a filename. | ||
| """ | ||
|
|
||
| filename = self.temp('not-fits.fits') |
There was a problem hiding this comment.
The original issue mentioned *.dat file but here you are testing with *.fits. Probably does not matter (if FITS code does not impose extension hard-coding) but I would like to know if this test actually fails in current master without this PR.
There was a problem hiding this comment.
The code does not use the extension afaik.
There was a problem hiding this comment.
@pllim - in one of my original commits I wrote a test - using the file name 'not-fits.fits' - and made sure that it failed before I changed any code. You can see this at
It was not tested against the current master branch, but whatever was master at the time.
| # of six by adding a reason string | ||
| @pytest.mark.skipif(six.PY2, reason='six.PY2') | ||
| @pytest.mark.parametrize('ignore', [False, True]) | ||
| @pytest.mark.parametrize('fileobj', [False, True]) |
There was a problem hiding this comment.
I actually don't understand this part. I skimmed through the conversation but none of it explains to me why fileobj needs to be a test input. Shouldn't you be checking to see if filename is a file object instead of an external input like this? Something like if not isinstance(filename, six.string_types): ... # a file object. Of course, then you have to first create the file object at the start of the test. Am I missing something here?
There was a problem hiding this comment.
Oh actually in my previous comment I did not mean to parametrize fileobj, but just to put the two with blocks in the same test, to test both with ignore_missing_end. Here it gives the same result but it generates the file four times.
There was a problem hiding this comment.
Response to @saimn 's comment:
Well, I realized that I really should check that the ignore_missing_end behavior is checked when a user has explicitly opened the file. Given the use of pytest.mark.parametrize I realized that I could get all versions written in a single - but ugly - test.
There was a problem hiding this comment.
Repsonse to @pllim comment
I originally had two tests, one where the file was passed as a filename and one where it was passed as a file object. To avoid some code repetition I combined them into a single test, and used a boolean flag (the fileobj parameter) to determine what was to be done. This was partly in response to my mis-understanding of a comment by @saimn
There was a problem hiding this comment.
Doing a file I/O 4x when 2x is sufficient is undesirable. I recommend you and @saimn sort out the misunderstanding before merge. Thanks!
There was a problem hiding this comment.
Just to note: the file sizes in question are very small, so it's not a large amount of I/O that is being duplicated.
|
I have unified the tests so that the file is only created once, and then the four different tests are run on it. if this passes I'll promise I'll get to squash it. |
|
In the end the same combination of parameters were tested I think, it's just that there are several way to test all combinations. But I like the latest version without |
Python 3 warns about open file handles on exit (via a ResourceWarning warning) which fits.open can create if given an invalid FITS file. This commit causes the opened file to be closed on error and adds a test to check that the warnings are no-longer raised. Care needs to be taken to not close a file handle if it was actually sent in, which required adding an extra bit of state to the FITS object (the close_on_error flag). It appears that compressed files do not cause this warning (and so no close is needed there).
|
Pushing a hopefully rebased and squashed PR. The last commit before this was 5848f80 . |
5848f80 to
86bf020
Compare
|
The three errors from the Travis tests do not appear to be related to this PR since they are:
|
|
Yeah errors are not related, it seems the astropy server is not in good shape. |
|
LGTM and I agree that failures are unrelated. Merging. Thank you! |
Remove ResourceWarnings when fits.open is given a non-FITS file (fix #6168)
This is not a complete fix - it's missing a changelog entry, could be squashed down into a single commit - but has been made to see if this change works - as asked for by @saimn at #6168 (comment)
The two main concerns I have - given that I don't know the code base - are:
Is the place I chose to insert the
closecall the best place for it? I started at the suggestion of ResourceWarning from astropy.io.fits.open when input isn't a valid FITS file #6168 (comment) and then looked up the calling chain a bitDo I only need to close the file handle, or are there other objects that need closing?
Edit 2017/06/18 I now believe this to be ready for review for inclusion. There are open questions about whether the location of the
closeis appropriate and whether it should be extended to other file types (mainly the compressed files). For the latter point I claim the current code is an improvement, and could be applied before working out what to do with the other file types, but I'm biased as this PR fixes my needs for now.Edit 2017/06/20 as far as I'm concerned I've addressed all the comments (and have squashed the PR to a more-comprehensible commit).