Skip to content

Refactor satpy.writers to separate modules #3122

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

djhoese
Copy link
Member

@djhoese djhoese commented May 7, 2025

This is my attempt at making Satpy's use of dependencies more flexible for users (me) that don't need all of the usual functionality of Satpy (readers -> resampling -> compositing -> writing) and therefore don't need all of satpy's usual dependencies.

The primary changes here are:

  1. Refactor satpy.writers to make it so trollimage doesn't get imported unless it is actually used and that we don't actually import things that aren't used. This saves an XRImage import but also a rasterio being installed as a dependency if trollimage is not installed (conda-forge's trollimage includes rasterio by default I think).
  2. Move flatten_dict from satpy.writers.utils to satpy.utils since it was being used by the satpy/dataset/metadata.py module. This means that writers don't have to be updated in the other parts of satpy that combine metadata (e.g. readers and composites).
  3. Move zarr import in the resample module so zarr isn't needed if you aren't using zarr-based caching.

Reviewers: Please be extra critical of the new module names in satpy.writers as I would like to have user import things from these module names rather than satpy.writers directly. This does mean users would be expected to do from satpy.writers.utils import get_enhanced_image. Also pay attention to if things in satpy.writers.utils should be somewhere else.

  • Closes #xxxx
  • Tests added
  • Fully documented
  • Add your name to AUTHORS.md if not there already

@djhoese djhoese self-assigned this May 7, 2025
@djhoese djhoese added the enhancement code enhancements, features, improvements label May 7, 2025
@djhoese djhoese requested a review from mraspaud as a code owner May 7, 2025 02:28
@djhoese djhoese requested a review from pnuu as a code owner May 7, 2025 02:28
@djhoese djhoese added the backwards-incompatibility Causes backwards incompatibility or introduces a deprecation label May 7, 2025
Copy link

codecov bot commented May 7, 2025

Codecov Report

Attention: Patch coverage is 93.81898% with 28 lines in your changes missing coverage. Please review.

Project coverage is 96.24%. Comparing base (78e88c4) to head (3f879f5).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
satpy/writers/overlay_utils.py 87.36% 12 Missing ⚠️
satpy/writers/utils.py 94.16% 8 Missing ⚠️
satpy/writers/base.py 90.76% 6 Missing ⚠️
satpy/writers/base_image.py 93.54% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3122   +/-   ##
=======================================
  Coverage   96.23%   96.24%           
=======================================
  Files         398      400    +2     
  Lines       57325    57373   +48     
=======================================
+ Hits        55168    55218   +50     
+ Misses       2157     2155    -2     
Flag Coverage Δ
behaviourtests 3.90% <40.17%> (+0.02%) ⬆️
unittests 96.33% <93.59%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coveralls
Copy link

coveralls commented May 7, 2025

Pull Request Test Coverage Report for Build 14935895156

Details

  • 429 of 458 (93.67%) changed or added relevant lines in 33 files are covered.
  • 8 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.006%) to 96.346%

Changes Missing Coverage Covered Lines Changed/Added Lines %
satpy/scene.py 2 3 66.67%
satpy/writers/base_image.py 30 32 93.75%
satpy/writers/base.py 60 66 90.91%
satpy/writers/utils.py 130 138 94.2%
satpy/writers/overlay_utils.py 84 96 87.5%
Files with Coverage Reduction New Missed Lines %
satpy/tests/utils.py 2 93.16%
satpy/tests/reader_tests/gms/test_gms5_vissr_l1b.py 3 98.67%
satpy/tests/reader_tests/gms/test_gms5_vissr_navigation.py 3 97.18%
Totals Coverage Status
Change from base Build 14862404225: 0.006%
Covered Lines: 55480
Relevant Lines: 57584

💛 - Coveralls

Comment on lines -152 to -165
def _determine_mode(dataset):
if "mode" in dataset.attrs:
return dataset.attrs["mode"]

if dataset.ndim == 2:
return "L"
if dataset.shape[0] == 2:
return "LA"
if dataset.shape[0] == 3:
return "RGB"
if dataset.shape[0] == 4:
return "RGBA"
raise RuntimeError("Can't determine 'mode' of dataset: %s" %
str(dataset))
Copy link
Member Author

Choose a reason for hiding this comment

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

FYI this function, as far as I can tell, isn't used anywhere so I deleted it (I did not move it to a submodule).

Copy link
Member

@pnuu pnuu left a comment

Choose a reason for hiding this comment

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

LGTM!

The only little thing I found, was that fallback import stuff in satpy.writers.__init__.py are not tested, so "we are not sure" they work. There could also be a deprecation warning or something o tell the users not to import directly from the writer subpackage.

@djhoese
Copy link
Member Author

djhoese commented May 7, 2025

You're right I should add tests for the import deprecation and add a specific sentence about how imports should be done now.

As mentioned on slack the naming is tricky. Regarding base_image.py versus image.py versus image_base.py, we could choose any one of these names but I was worried about making people think the module was for a standalone reader like "geotiff" rather than a set of base classes or utilities. Additionally, I wanted to move get_enhanced_image and to_image to the base image module but it seems odd as they are image related but not image writer specific.

Copy link
Member

@sfinkens sfinkens left a comment

Choose a reason for hiding this comment

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

Nice work! Especially the "has been moved" warnings 👍 Looking at the module names you could maybe rename writers.cf_writer to writers.cf.

@ameraner
Copy link
Member

ameraner commented May 8, 2025

Looks good to me too! Moving the get_enhanced_image will break quite a lot of codes I suspect (e.g. we use it a lot when inserting satpy composites into cartopy), so indeed the warning message is very useful and we should leave it there for a good while :)

@djhoese
Copy link
Member Author

djhoese commented May 8, 2025

Moving the get_enhanced_image will break quite a lot of codes I suspect

Yes @ameraner, I agree. That's why I'm really concerned about getting the submodules and what goes in those submodules right before merging this. Like maybe get_enhanced_image should really go in the satpy/enhancements/ subpackage. to_image too.

Comment on lines +942 to +943
from satpy.writers.utils import get_enhanced_image

Copy link
Member

Choose a reason for hiding this comment

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

Two questions/comments on function-level imports:

  • Can we have some guidance on what imports shall be avoided on a module level? I'm confused why this would apply to an import from within satpy.
  • Does this need to be tested? I can imagine someone (such as me) might easily re-introduce an import trollimage on a module level somewhere, and I don't think any test would warn against this.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're absolutely right. This should probably be guarded against on the callee side (inside get_enhanced_image). For now I went with the simple rule of "if it is only used by a few things inside the module then import it in-function". What I think we could do, especially after Tuesday's meeting this week, is actually remove trollimage as a hard requirement and have a test environment that runs "minimal" tests in a "minimal" environment.

The biggest problem with this particular change that you commented on is that satpy/composites/__init__.py is waaaaaay too big and needs to be split up by purpose. Similar to this PR there should be warnings on import and no real code in the __init__.py. Then it wouldn't matter much if you put an import like get_enhanced_image or zarr or even some other third-party package at the module level as that sub-module should only be imported when used.

@djhoese
Copy link
Member Author

djhoese commented May 12, 2025

I'm realizing after thinking about the "base_image.py" -> "image.py" suggestion that was either here or on slack, the "satpy/writers/base.py" could be just "satpy/writers/writer.py". That might be nice in that it keeps "base" and "core" out of the module naming, but also might make it difficult for new users/contributors to find the base classes. "satpy/writers/core/writer.py"? Maybe that's going too far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backwards-incompatibility Causes backwards incompatibility or introduces a deprecation component:writers enhancement code enhancements, features, improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants