Skip to content
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

Load Image from bytes #1588

Merged
merged 11 commits into from
Oct 5, 2022
Merged

Load Image from bytes #1588

merged 11 commits into from
Oct 5, 2022

Conversation

bruno-rino
Copy link
Contributor

@bruno-rino bruno-rino commented Sep 12, 2022

Currently Image loads files by file path or URL. This PR adds a bytes object as an option.
Refs #1348

I'd like feedback on whether overloading the positional argument for the Image constructor is well received. Its name, path, makes no sense anymore. I changed it to value.

Implemented on all backends, but only tested on macOS.

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

Signed-off-by: Bruno Rino <[email protected]>
Signed-off-by: Bruno Rino <[email protected]>
Signed-off-by: Bruno Rino <[email protected]>
Signed-off-by: Bruno Rino <[email protected]>
Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

General direction looks good; one comment inline about the change to the API for Image().

One other suggestion relates to the example. The example test case works to demonstrate the implemented behavior, but isn't a very realistic "real world" case.

I'd like to see an example that is closer to "reality" - e.g., generating a new image with Pillow, storing it as bytes, then showing that image. In addition to proving that the "real world" use case has been satisfied, it serves as useful documentation-by-example to anyone wondering how to use the API.

self.path = path
def __init__(self, value=None, path=None):
if path is not None:
value = path # TODO: code to deprecate path
Copy link
Member

Choose a reason for hiding this comment

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

I can see what you're doing here, but the error handling on this is incomplete. It allows specifying both value and path, and doesn't catch the Image() case (at least, not before crashing later).

I'd also suggest changing the function prototype to:

def __init__(self, value=None, *, path=None, data=None):

If you specify value, you get full "autodetection" behavior; if you specify path, you get the existing "path or url" conversion; and if you specify data, it's assumed to be in the right format. Specifying more than one argument would be an error. This doesn't have to be a deprecation path, either - it doesn't seem unreasonable as an ongoing API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd never encountered that lonely star in the args before. Powerful stuff.

If your preference is to preserve separate path and data arguments, I personally would prefer this function prototype:

def __init__(self, path=None, data=None):

Simpler, more obvious. One way of doing things. More Zen.

But you're the BDFL. Let me know.

Copy link
Member

Choose a reason for hiding this comment

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

That's a good point - I got so focussed on the change introducing value that I missed the obvious option. Let's go with (path=None, data=None).

FYI - there's an analogous / that matches * in args. * forces all subsequent arguments to be kwargs; / forces all prior arguments to be positional. The full form of a function is effectively:

def name(positional_only_parameters, /, positional_or_keyword_parameters, *, keyword_only_parameters):

See PEP570 (and PEP457) for details.

Copy link
Member

Choose a reason for hiding this comment

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

In that case, data should probably be a keyword-only argument, while path can continue to be either keyword or positional, for backward compatibility. In other words:

def __init__(self, path=None, *, data=None):

@bruno-rino bruno-rino marked this pull request as ready for review October 4, 2022 17:44
@codecov
Copy link

codecov bot commented Oct 5, 2022

Codecov Report

Merging #1588 (f9ec63c) into main (dfc7273) will decrease coverage by 9.76%.
The diff coverage is n/a.

Impacted Files Coverage Δ
src/core/toga/handlers.py 40.00% <0.00%> (-6.67%) ⬇️
src/core/toga/platform.py 17.64% <0.00%> (-5.89%) ⬇️
src/core/toga/style/applicator.py 84.21% <0.00%> (-5.27%) ⬇️
src/core/toga/style/pack.py 87.08% <0.00%> (-2.50%) ⬇️
src/core/tests/test_image.py
src/core/toga/images.py
src/dummy/toga_dummy/images.py
src/core/tests/widgets/test_base.py
src/core/tests/test_documents.py
src/core/tests/sources/test_tree_source.py
... and 114 more

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

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

I've made a couple of small tweaks to the example, but otherwise: This is great - thanks!

@freakboy3742 freakboy3742 merged commit 9a292f8 into beeware:main Oct 5, 2022
@bruno-rino bruno-rino deleted the image_from_bytes branch October 5, 2022 06:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants