-
Notifications
You must be signed in to change notification settings - Fork 14
Fix: fix loadimage function to robustly load image #34
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #34 +/- ##
============================================
+ Coverage 50.00% 100.00% +50.00%
============================================
Files 2 3 +1
Lines 18 48 +30
============================================
+ Hits 9 48 +39
+ Misses 9 0 -9
🚀 New features to boost your workflow:
|
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.
@stevenhua0320 I can't see the tests for this. I guess that you started by writing a test, which is our workflow, right? :)
you can work with @zmx27 to get help with this, but
- there is a tif file in
docs/examples/data(actually, that is where it should be) - in
conftest.py, copy this into theuser_filesystemsoemwhere - use this fixture in test to try the different scenarios (directory is cwd, directory is absolute path, directory is relative path0
Actually I have tested with these scenarios on my local machine and they generated the correct matrix of that same |
yes, this is our workflow. Agree on the test. Only then write the function. The test captures the behavior we want (so in this case, three different successful cases). We can also think about how to handle unsuccessful cases. Probably give a helpful error message for FileNotFound or whatever. The error messages all have the form "what happened. What to do to fix it", so here it would be something like |
|
@sbillinge I'm designing for the test, right now it works well
Got the idea, in the original function, it has the |
|
that's the right idea. We would usually build the desired user-filesystem as a fixture in conftest.py. In this way it can be reused across different tests. Take a look in |
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.
Se comments
tests/conftest.py
Outdated
| cwd_dir = base_dir / "cwd_dir" | ||
| cwd_dir.mkdir(parents=True, exist_ok=True) | ||
| test_dir = base_dir / "test_dir" | ||
| for d in (input_dir, home_dir, test_dir): |
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.
Don't use a single letter for the iterator. Always choose to make it more readable... E.g., for dir in (
I am not sure if you used an LLM for help. OK if you did, but try and work hard to bring the code up to group standards before pushing. It is really really tedious reviewing code from chatgpt....
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, that is my habit of indexing object when writing for loops. I should stop this formatting and follow more readable format.
tests/conftest.py
Outdated
| def example_tif(): | ||
| """Locate the example TIFF file relative to repo root.""" | ||
| tif_path = Path("../docs/examples/KFe2As2-00838.tif").resolve() | ||
| if not tif_path.exists(): |
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.
Ooooh, don't be doing that. We want tests to fail of something is wrong
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 will delete this in the next PR.
tests/conftest.py
Outdated
| return LoadImage(cfg) | ||
|
|
||
|
|
||
| # ---------------------------------------------------------------------- |
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.
Please remove all these comment blocks
tests/conftest.py
Outdated
| # LoadImage test setup | ||
| # ---------------------------------------------------------------------- | ||
| @pytest.fixture(scope="module") | ||
| def example_tif(): |
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.
we don't need a fixture for htis. Just copy the file over as you build the filesystem above.
tests/conftest.py
Outdated
| return tif_path | ||
|
|
||
|
|
||
| @pytest.fixture(scope="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 am not sure what this is doing but it doesn't seem appropriate to be building a fixture using one of hte functions that we will be testing. Just delete this.
As a heads-up, we don't do any testing in conftest.py we just build fixtures.
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, you mean we need to create a new test_loadimage.py and do all the tests there? If yes then I migrate the test to there.
tests/conftest.py
Outdated
| return loader.loadImage(example_tif) | ||
|
|
||
|
|
||
| @pytest.fixture |
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.
see above, this should also be deleted.
tests/conftest.py
Outdated
| # Unified test | ||
| # ---------------------------------------------------------------------- | ||
|
|
||
|
|
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.
use pytest.mark.parametrize for the different cases. Please see examples in diffpy.cmi etc.
tests/conftest.py
Outdated
| ): | ||
| home_dir = user_filesystem["home"] | ||
| test_dir = user_filesystem["test"] | ||
| monkeypatch.setenv("HOME", str(home_dir)) |
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.
we don't use monkeypatch but we use pytest mock if it is needed.
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.
please see comments.
I have seen it and I'm currently making edits so that it would follow group's standard. |
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.
pls see comments
tests/test_loadimage.py
Outdated
| return image_loader.loadImage(tif_path) | ||
|
|
||
|
|
||
| def build_loadimage_path(case_tag, home_dir): |
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.
your test seems to have logic in it, but we don't really want that. the purpose of the tests is to encode behavior and then test logic in the function. The tests just have to capture behavior. please see below.
tests/test_loadimage.py
Outdated
|
|
||
|
|
||
| load_image_param = load_image_params = [ | ||
| ("abs", PROJECT_ROOT / "docs/examples/KFe2As2-00838.tif"), |
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 is where we capture the behavior. Please see the other projects for examples, but we want first a comment, e.g.
# case 1: just filename of file in current directory. expect function loads tiff file from cwd
then the paramaterize looks something like (input, output). In this case the input would be "KFe2As2-00838.tif" and the output would assert something that assures us that the file was correctly read.
Then similarly for case 2 # case 2: absolute file path to file in another directory. expect file is found and correctly read
and case 3 would be the relative path.
the test would look soething like
@pytest.mark.parametrize("input, expected")
def test_load_image_cases(input, expected, , user_filesystem):
copy the test tiff to home_dir
copy the test tiff to test_dir # these two lines could be done in conftest.py if we want
cd to home_dir
actual = load_image(input)
some asserts that compare actual and expected things.
|
@sbillinge Professor Simon, If there is anything I could modify in the test file please give me more feedback and I would continue to change it. |
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 cases captured in the comments are now good, except Case 4 which I think should result in a crash, but the code needs some tlc. Please could you reach out to @zmx27 and work together on the implementation?
OK,actually in Case 4 in the implementation it would return the message like "file {filename} not found, please type in correct file name..." something like that and then return nothing. That's why in the test cases I check the datatype of what |
yes, I know that was your intent with case 4. I would rather that the users get that message but in a crash. In that case it would be in 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.
Good progress. Still some things in the wrong direction.
We do not need tests/cwd/example.tiff, tests/home/example.tiff, or tests/test/example.tiff so please delete these. We will make a clean PR later so we don't add and remove these files, but for now, please delete.
tests/test_loadimage.py
Outdated
| )() | ||
| loader = LoadImage(cfg) | ||
|
|
||
| if expected: |
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.
don't have logic in your test
tests/test_loadimage.py
Outdated
| @pytest.mark.parametrize("input_path, expected", load_image_param) | ||
| def test_load_image_cases(input_path, expected, user_filesystem): | ||
| base_dir, home_dir, cwd_dir, test_dir = user_filesystem | ||
| test_file_dir = Path(__file__).parent |
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.
you seem to be working not in tmpdir, so don't be using test_file_dir in the tests. In fact don't even create this directory.
We will create all the directories we need in tmp_dir. Actually, we already have them, they are passed in from user_filesystem.
so we will move to cwd:
os.chdir(cwd_dir)
and we need to move our example file from docs/examples to the different places we will use it. So something like
source_dir = Path(__file__).parent / "docs" / "examples" / "data"
shutil.copy(source_dir / "example.tiff", test_dir)
shutil.copy(source_dir / "example.tiff", .)
So we have an example tiff in cwd_dir (which is also the current working directory because we cd'd there) and also in ../test_dir.
After this we are fully working in the tempdir that will be built up before every test and removed after every test automatically.
Then we run as
actual_image = load_image(input_path)
assert something_about_actual_image == expected
we can decide different things to assert. That the shape is right, the total integrated intensity, and so on.
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.
Professor, you are right. Now I learned how to deal with the conftest for user_system and now it would not create additional files in the step of copying as it is in a tmp_path. I also edited the testing here to both handle good case and bad case without using logic here. I would commit a new PR not introducing additional example.tiff in directory immediately.
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.
Still a bit more to go. We are testing relative paths but not absolute. Also, please see comments
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.
please see comments.
news/load-image-fix.rst
Outdated
| **Fixed:** | ||
|
|
||
| * <news item> | ||
| * Fixed `loadimage` function in any directory. |
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.
load_image() function correctly finds files when passed a relative path
src/diffpy/srxplanar/loadimage.py
Outdated
| pic = np.array(pic[::-1, :]) | ||
| return pic | ||
|
|
||
| def loadImage(self, 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.
let's make the function have a pep8 compliant name. We may as well fix these when we touch the function.
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.
pls see comments
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.
pls see comments
Updated expected values for image loading tests and corrected the source file 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 edited the file to show what I had in mind. It is breaking the test now for sure but hopefully this gives you the idea.
|
btw, I would like to get rid of the cfg clause too. We should decide what we want the function to do explicitly and then test that, so we don't want to put logic in the test that makes the function pass. |
Got the idea, I would integrate the hard-coded one for the first and last point of example tiff file, change it to 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.
ok, good progress, we are getting there.
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.
pls see comments
tests/test_load_image.py
Outdated
| import os | ||
| import shutil | ||
| from pathlib import Path | ||
| from unittest.mock import Mock |
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.
we don't ever use unittest.mock, we use pytest.mock, but as discussed below, I don't think we should be mocking anything here.
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.
Few more questions on the function now. Can you suggest how we want it to handle flipping?
src/diffpy/srxplanar/loadimage.py
Outdated
| image = np.load(filenamefull) | ||
| else: | ||
| image = open_image( | ||
| str(filenamefull) |
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.
Per the conversation above, didn't we want to remove this str? That was my point actually
src/diffpy/srxplanar/loadimage.py
Outdated
| except FileNotFoundError: | ||
| time.sleep(0.5) | ||
|
|
||
| image = self.flip_image(image) |
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.
Per conversation above, move this or of the function, or allow flipping as optional inputs.
Closes #4 @sbillinge Ready to review