Skip to content

UnboundLocalError: local variable 'err_code' referenced before assignment #2383

Description

@NotSoSuper

What did you do?

Load a GIF and attempted to save each frame to a file

What did you expect to happen?

Each frame of the gif is saved to a different image file in a directory

What actually happened?

The loop errored before completion

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/discord/ext/commands/core.py", line 50, in wrapped
    ret = yield from coro(*args, **kwargs)
  File "/root/discord/mods/Fun.py", line 313, in gmagik
    for image in glob.glob(gif_dir+"*_{0}.png".format(rand)):
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/root/discord/mods/Fun.py", line 232, in do_gmagik
    try:
  File "/usr/local/lib/python3.6/site-packages/PIL/Image.py", line 1698, in save
    self.load()
  File "/usr/local/lib/python3.6/site-packages/PIL/ImageFile.py", line 242, in load
    if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
UnboundLocalError: local variable 'err_code' referenced before assignment

What versions of Pillow and Python are you using?

Latest PIL from PyPy, Python 3.6

Replicated just using PIL from repl.

try:
	frame = PIL.Image.open(gif)
except:
	return '\N{WARNING SIGN} Invalid Gif.'
if frame.size >= (3000, 3000):
	os.remove(gif)
	return '\N{WARNING SIGN} `GIF resolution exceeds maximum >= (3000, 3000).`'
count = 0
while frame:
	frame.save('{0}{1}_{2}.png'.format(gif_dir, count, rand), 'GIF')
	count += 1
	try:
		frame.seek(count)
	except EOFError:
		break

GIF In question: https://cdn.discordapp.com/attachments/272432272949510144/275311320017010709/gmagik.gif (can give more samples if needed)

Activity

  1. wiredfool commented on Jan 29, 2017

    @wiredfool
    Member

    Thanks, a couple of people have reported this, but your report is the first to include a triggering image.

  2. NotSoSuper commented on Jan 29, 2017

    @NotSoSuper
    Author

    No problem, I might wanna also include that the gif was processed before (loaded into PIL, frames extracted, Wand/Imagemagick effects applied and then stitched together with ffmpeg) using the same function.

  3. hugovk commented on Jan 29, 2017

    @hugovk
    Member

    GIF In question: https://cdn.discordapp.com/attachments/272432272949510144/275311320017010709/gmagik.gif

    This URL gives me:

    <?xml version='1.0' encoding='UTF-8'?><Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message></Error>

    Please could you attach the image to this issue?

  4. NotSoSuper commented on Jan 29, 2017

    @NotSoSuper
    Author

    That's weird, still loading for me after cache reset but Discord CDN tends to be weird, here:
    gmagik

  5. wiredfool commented on Feb 7, 2017

    @wiredfool
    Member

    The basic problem is that the second frame has an extent of 256x249, while the first frame is 256x248.

    -> decoder.setimage(self.im, extents)
    (Pdb) extents
    (0, 0, 256, 249)
    (Pdb) self.im.size
    (256, 248)
    (Pdb) n
    ValueError: 'tile cannot extend outside image'
    > /Users/erics/tests/build/bdist.macosx-10.11-x86_64/egg/PIL/ImageFile.py(197)load()
    -> decoder.setimage(self.im, extents)
    

    This is in a try/except, which simply swallows the error.

                for decoder_name, extents, offset, args in self.tile:
                    decoder = Image._getdecoder(self.mode, decoder_name,
                                          args, self.decoderconfig)
                    seek(offset)
                    try:
                        decoder.setimage(self.im, extents)
                    except ValueError:
                        continue

    Which bounces out, and then we hit the err_code not defined.

    I know that in other image types (e.g., TIFF) when we have multiple frames, we allow for the new image to be a different size than the old one. TIFFs don't have disposal options that GIFs have, so there's a little more complication in the GIF case.

    I'd propose that if the extents are outside the existing image, we should increase the canvas size to cover the union of the existing and next frame. (as long as the extents aren't negative. I think that we should bounce that error).

    Additionally, we should not bury errors of this sort. They should probably be raised and allowed to stop the processing.

  6. NotSoSuper commented on Feb 10, 2017

    @NotSoSuper
    Author

    Ah, well that makes more sense now.

    Increasing canvas size does sound like a good way to go unless there are other complications, thanks again!

  7. added
    BugAny unexpected behavior, until confirmed feature.
    on Feb 17, 2017
  8. radarhere commented on May 2, 2019

    @radarhere
    Member

    Negative extents aren't actually possible, as the offsets are unsigned integers.

    x0, y0 = i16(s[0:]), i16(s[2:])

    def i16le(c, o=0):
    """
    Converts a 2-bytes (16 bits) string to an unsigned integer.

    I've created PR #3822 to resolve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugAny unexpected behavior, until confirmed feature.GIFPNG

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions