-
Notifications
You must be signed in to change notification settings - Fork 260
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
WIP: Migrate load, save, class_map, ext_map to class properties and methods #329
Conversation
if not klass.is_valid_extension(ext): | ||
return False, sniff | ||
elif (getattr(klass.header_class, 'sniff_size') is None or | ||
getattr(klass.header_class, 'is_header') is None): |
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 if None
rather than do a hasattr
, so that properties can be either unset, or reset to None
(which is easier when subclassing).
Small cleanups in ab2081b. Given that all |
klass = next(iter(valid_klasses)) | ||
except StopIteration: # if iterator is empty | ||
raise ImageFileError('Cannot work out file type of "%s"' % | ||
filename) |
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 next(iter(filter(...)))
is awkward. Alternatives:
valid_klasses = list(filter(lambda klass: klass.is_valid_extension(ext), all_image_classes))
valid_klasses = [klass for klass in all_image_classes if klass.is_valid_extension(ext)]
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 go for the second, since we want a list anyway...
Yes. I used that branch to make a suggested commit, but no need to have a PR for it. |
from .minc2 import Minc2Image | ||
from .freesurfer import MGHImage | ||
from .filename_parser import splitext_addext | ||
from .volumeutils import BinOpener |
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.
Should be:
from .openers import ImageOpener
Thanks @effigies -- I can see that I needed to be more careful about integrating my other PR into this one. I'll use your notes and go through things a bit myself to make sure! |
Getting there. Added back some tests. Want to clean things up a bit further (e.g. two of the properties I put on the individual classes are really test-only, and should live in the associated tests). I also need to review the test coverage, to see why it's dropped so significantly. |
@bcipolli A couple small fixes in a863949. Mostly the same as earlier, but rebased for convenience, and changed the MINC1 test to reduce the space of false positives to other NetCDF images. Relatedly, I think we may want to add documentation to |
@effigies That's great--thanks for the tweaks! An easier workflow for me would be if you make PRs directly against my branch, and use your comments as the PR message. Then I could just iterate with you there and merge, rather than cherry picking manually (and potentially losing track of things). Would that work for you? |
Sure, that works. Was assuming we'd want to keep conversation in one place, but I'm fine with anything. |
I'm pretty happy with this. To reiterate this issue with Also a couple style issues I'll PR on your branch in a minute. |
none=None, # pass None as the sniff, should query in fn | ||
empty='', # pass an empty sniff, should query in fn | ||
irrelevant='a' * (sizeof_hdr - 1), # A too-small sniff, query | ||
bad_sniff='a' * sizeof_hdr).items(): # Bad sniff, should fail. |
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 these be bytes
objects (b'a')? They'll either fail or trigger a new sniff anyway, so maybe not a big deal, but if you want them to fail for the right reasons on Python 3...
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.
Just to note, @effigies added a change for this (and another test issue) that I've merged in.
@matthew-brett How do you feel about tests on deprecation warnings? Seem like they might be excessive, but I think that's part of the test coverage decrease. |
@@ -892,6 +892,17 @@ def _chk_pixdims(hdr, fix=False): | |||
rep.fix_msg = ' and '.join(fmsgs) | |||
return hdr, rep | |||
|
|||
@classmethod | |||
def is_header(klass, binaryblock): |
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.
How about may_contain_header
?
@matthew-brett Incorporated your suggestions. Let me know if my approach to Looks like we're back up to positive test coverage, so that's good. |
@@ -212,43 +203,3 @@ def read_img_data(img, prefer='scaled'): | |||
if prefer == 'scaled': | |||
return hdr.data_from_fileobj(fileobj) | |||
return hdr.raw_data_from_fileobj(fileobj) | |||
|
|||
|
|||
def which_analyze_type(binaryblock): |
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.
Sorry man - I know this is anal - but this one's part of the API too, technically.
No problem. I'm mostly writing English right now, so an excuse to procrastinate with code is great. @matthew-brett Didn't get rid of the |
@matthew-brett Just a bump, in case you've got a bit of time to look this over. |
Yes, sorry, thanks for the bump. Sorry to be ignorant, but won't the The image API has now expanded to include What do you think about the case where the sniff can't make any sense transferred from on image type to the next? At the moment, this can't happen, but sort of by accident. For example, if we are trying to load a Admittedly, this can't easily happen, but it does seem like an unstated assumption in the code, that it can't happen. I was thinking of something like this, to make that more explicit
|
@matthew-brett Thanks for thinking so carefully. I understand the issue you're wishing to solve. I'd prefer a solution that uses the class metadata to invalidate sniffs. Two ways to do this:
|
Passing the filename with the sniff seems a bit ugly. How would you keep track of the filename in the load function? I guess you'd have to return it from the |
No. It will return False because slicing beyond the end of a
I'll think a bit about your other concern as I can. |
Ah - yes - but isn't False the right answer in this case? |
It is. I think it was a desire for consistent behavior across image classes that led here. There's a commit in the stack that removed the As to your other concern, I apparently have a couple thoughts already: If passing the path back and forth is too ugly, what about switching to
On the off chance you have the sequence checks-foo, checks-bar, checks-foo, I believe it's already pretty much strongly assumed that a header and image file are assumed to have the same basename, so the |
Sorry - keep on getting swamped in nipy stuff. Thinking about it, maybe the (sniff, filename) tuple isn't too bad. I guess then you'd have to check whether the filename is the correct metadata filename for each image type, but that seems fine. So it's just redfining a sniff as as (bytes, filename) pair, which seems OK. I have a vague preference for not raising errors in the I'll be more attentive here now - but please feel free to remind me at short (one day) intervals if I'm slacking. |
I don't have a strong opinion here, but I would say if we're going to get rid of the exception in Minc1, then checks like this should return |
Agree about the consistency of checks. |
Currently doesn't occur, but possible that different images with a common extension could have different header extensions. This change checks that the header file name is the same before reusing a sniff.
Okay, just updated the doc string and killed the exceptions. |
I think this is looking good now. Last thing - would you mind adding the tests for the expanded Image API in I realize that test function is a bit hairy, so let me know if I can help - I should have time today and tomorrow. |
How about adding something like the following to
|
Aren't we insisting that every image class have Should be |
The PAR/REC test didn't have an |
Not all tests have image_maker attributes, so add klass attribute to test path_maybe_image. Checks structure of return values on calls to example images.
Ah yes, good point. I think the PR is ready, if the tests pass. Ben - any further comments before I merge? |
👍 |
MRG: Migrate load, save, class_map, ext_map to class properties and methods This is to address #317 and #323 (and aims to supercede PR #319), both issues around image-specific code being distributed across files, rather than localized in the class definition. Changes here include: * Removing image-specific code from `nib.load` and `nib.save` functions, moving the logic into `ImageKlass.is_image` and `HeaderKlass.is_header` class functions. * Deprecating `class_map` and `ext_map` objects, and moving necessary info to properties defined on the image classes themselves.
Thanks a lot for your patience - sorry it took so long. |
MRG: Migrate load, save, class_map, ext_map to class properties and methods This is to address nipy#317 and nipy#323 (and aims to supercede PR nipy#319), both issues around image-specific code being distributed across files, rather than localized in the class definition. Changes here include: * Removing image-specific code from `nib.load` and `nib.save` functions, moving the logic into `ImageKlass.is_image` and `HeaderKlass.is_header` class functions. * Deprecating `class_map` and `ext_map` objects, and moving necessary info to properties defined on the image classes themselves.
This is to address #317 and #323 (and aims to supercede PR #319), both issues around image-specific code being distributed across files, rather than localized in the class definition.
Changes here include:
nib.load
andnib.save
functions, moving the logic intoImageKlass.is_image
andHeaderKlass.is_header
class functions.class_map
andext_map
objects, and moving necessary info to properties defined on the image klasses themselves.Things needing to be done in this PR:
Things that need to be done in a future PR:
.mgz
infiles_types
as an'image'
is a bit funky, but will take thought and work to do cleanly. I have an idea of what to do, but would prefer to do a subsequent PR on it as this one is already quite sizable.