Forum Archive

How to debug crash of image script when it's called as extension

halloleooo

I have a script which uses Pillow to join images - when I call it from Pyhonista itself (with some default images) it runs fine, when I run it (as it is intended) from the Share menu of, say, the Files app, it crashes.

Problem is I can't find any trace of the crash (no console information or exception backtrace). How can I find out why the script crashed and to what point it got?

mikael

@halloleooo, you could explicitly write lines to a file at different points of your script, starting with a version that does nothing much, before adding the image handling.

pathlib.Path.write_text is a convenient one-liner for this.

cvp

@halloleooo I sometimes noticed, in appex mode, that an error was lodged in the console but was hidden by a ui.view. By closing the view, without terminating the program, there was a way to see the log.

halloleooo

@mikael @cvp Thanks for the suggestions. I will try them.

My own investigation shows, the issue seems to be related to memory use of the images. Not really big stuff, but the size seems to be the culprit:

  • When I use 2 images with 1MB each it crashes.
  • When I use 2 images with 10Kb each it works.

All 4 images come from the Files app.

mikael

@halloleooo, from what I read, share extensions should have a 120 MB memory limit, not sure how much Pythonista takes.

halloleooo

@mikael Mmmh, interesting. So why does my share extension crashes with (mere) 1MB images???

cvp

@halloleooo advice: if you want to join photos, don't run in appex mode and use photos module to pick two images. We always have had memory problems with big photos in appex mode.

cvp

@halloleooo I think that a pil image of 10000x10000 pixels uses 10000x10000x4 bytes = 400 MB
even if your compressed jpeg is a 6MB file

halloleooo

@cvp Aha! that makes total sense!

But how can I not run in appex mode, but still join images from anywhere? I thought that's what share extensions are for...

cvp

@halloleooo perhaps find a way without passing via pil, for example, via ui.image_context?

cvp

@halloleooo if you want a join of original pictures, I think that even ui.image_context will use a lot of memory, but if you want a smaller jointed image, it could be possible to work with resized images.

cvp

@halloleooo please, try this one in the share sheet

import appex
import ui
import io
from PIL import Image

def pil2ui(imgIn):
    with io.BytesIO() as bIO:
        imgIn.save(bIO, 'PNG')
        imgOut = ui.Image.from_data(bIO.getvalue())
        del bIO
    return imgOut

ui_images = []  

# assume two images are shared
files = appex.get_attachments()
for f in files:
    pil = Image.open(f)#,mode='r')
    ui_image = pil2ui(pil)
    del pil
    w,h = ui_image.size
    wi = 400
    hi = wi*h/w
    with ui.ImageContext(wi,hi) as ctx:
        ui_image.draw(0,0,wi,hi)
        del ui_image
        ui_resize = ctx.get_image()
        ui_images.append(ui_resize)
        del ui_resize       

w1,h1 = ui_images[0].size
w2,h2 = ui_images[1].size
# assume images have same height
with ui.ImageContext(w1+w2,h1) as ctx:
    ui_images[0].draw(0,0,w1,h1)
    ui_images[1].draw(w1,0,w2,h2)
    ui_image_joined = ctx.get_image()
w,h = ui_image_joined.size
v = ui.ImageView()
v.frame = (0,0,400,400*h/w)
v.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
v.image = ui_image_joined
v.present('')
halloleooo

@cvp Thanks for the detailed script.

One thing I am wondering about is: You get the images from the share extension as PIL images via

appex.get_attachments()

and then convert them to UI images. Isn't there a way to get them directly as UI images? I thought UI image is the iOS-native format...

cvp

@halloleooo you're right but we got some problems with images in appex mode, thus...
Else, I use this where you can set type you want

img = appex.get_image(image_type='pil')
JonB

Doesn't appex provide a get_ui_image function? That will be much lighter weight than going through PIL and back.

cvp

@JonB sure, he knows that, but in the past, we got some problems with it.

Édit: believe me, if we go through pil, it is not for the pleasure to do it.

halloleooo

@cvp So what are the problems with get_ui_image?

cvp

Correction: appex does not have a get_ui_image

cvp

We mixed with asset.get_ui_image

cvp

I have been disturbed only because the post comes from @JonB

cvp

@halloleooo doc dixit, you could try with

img = appex.get_image(image_type='ui')
#files = appex.get_attachments()
#for f in files:
files = appex.get_images(image_type='ui')
for ui_image in files:
    #pil = Image.open(f)#,mode='r')
    #ui_image = pil2ui(pil)
    #del pil
halloleooo

I think the Pyhonista docs mentions appex.get_ui_image(). Is this wrong?

cvp

@halloleooo modif just above also crashes with two big photos...

cvp

@halloleooo said:

I think the Pyhonista doco mentions appex.get_ui_image(). Is this wrong?

The internal doc, reached via help, and the source of appex.py do not mention it

cvp

@halloleooo but, as I said previously, asset.get_ui_image exists

cvp

@halloleooo print dir(appex) to confirm

cvp

@halloleooo I said"modif just above also crashes with two big photos..." but with pil it works

cvp

@halloleooo ok, now, Forget all, this works, by commenting the del ui_image
and is quicker than converting pil
even with wi=1000 with two photos of 2600x4000 pixels

# assume two images are shared
#files = appex.get_attachments()
#for f in files:
files = appex.get_images(image_type='ui')
for ui_image in files:
    #pil = Image.open(f)#,mode='r')
    #ui_image = pil2ui(pil)
    #del pil
    w,h = ui_image.size
    wi = 400
    hi = wi*h/w
    with ui.ImageContext(wi,hi) as ctx:
        ui_image.draw(0,0,wi,hi)
        #del ui_image
        ui_resize = ctx.get_image()
        ui_images.append(ui_resize)
        del ui_resize       
JonB

@cvp, sorry, I did not actually try it -- per the release notes there is supposed to be appex.get_ui.image().

didn't actually look -- but