We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
No console warning when setting Image.source = "some/file/that/does/not/exist"
When you load a file into an Image widget by setting .source = "some/file/name.png" there is a message printed on the console:

[DEBUG  ] [ImagePygame ] Load <data/tiles/dirt.png>

If you set .source = to a filename that does not exist, there is no feedback. No debug message is printed, and the Image widget remains blank.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kivy/uix/image.py`
Content:
```
1 '''
2 Image
3 =====
4 
5 The :class:`Image` widget is used to display an image::
6 
7     wimg = Image(source='mylogo.png')
8 
9 Asynchronous Loading
10 --------------------
11 
12 To load an image asynchronously (for example from an external webserver), use
13 the :class:`AsyncImage` subclass::
14 
15     aimg = AsyncImage(source='http://mywebsite.com/logo.png')
16 
17 Alignment
18 ---------
19 
20 By default, the image is centered and fit inside the widget bounding box.
21 If you don't want that, you can inherit from Image and create your own style.
22 
23 For example, if you want your image to take the same size of your widget, you
24 can do::
25 
26     class FullImage(Image):
27         pass
28 
29 And in your kivy language file, you can do::
30 
31     <FullImage>:
32         canvas:
33             Color:
34                 rgb: (1, 1, 1)
35             Rectangle:
36                 texture: self.texture
37                 size: self.size
38                 pos: self.pos
39 
40 '''
41 
42 __all__ = ('Image', 'AsyncImage')
43 
44 from kivy.uix.widget import Widget
45 from kivy.core.image import Image as CoreImage
46 from kivy.resources import resource_find
47 from kivy.properties import StringProperty, ObjectProperty, ListProperty, \
48         AliasProperty, BooleanProperty, NumericProperty
49 from kivy.loader import Loader
50 
51 
52 class Image(Widget):
53     '''Image class, see module documentation for more information.
54     '''
55 
56     source = StringProperty(None)
57     '''Filename / source of your image.
58 
59     :data:`source` is a :class:`~kivy.properties.StringProperty`, default to
60     None.
61     '''
62 
63     texture = ObjectProperty(None, allownone=True)
64     '''Texture object of the image.
65 
66     Depending of the texture creation, the value will be a
67     :class:`~kivy.graphics.texture.Texture` or
68     :class:`~kivy.graphics.texture.TextureRegion` object.
69 
70     :data:`texture` is a :class:`~kivy.properties.ObjectProperty`, default to
71     None.
72     '''
73 
74     texture_size = ListProperty([0, 0])
75     '''Texture size of the image.
76 
77     .. warning::
78 
79         The texture size is set after the texture property. So if you listen on
80         the change to :data:`texture`, the property texture_size will not be
81         up-to-date. Use self.texture.size instead.
82     '''
83 
84     def get_image_ratio(self):
85         if self.texture:
86             return self.texture.width / float(self.texture.height)
87         return 1.
88 
89     mipmap = BooleanProperty(False)
90     '''Indicate if you want OpenGL mipmapping to be applied on the texture.
91     Read :ref:`mipmap` for more information.
92 
93     .. versionadded:: 1.0.7
94 
95     :data:`mipmap` is a :class:`~kivy.properties.BooleanProperty`, default to
96     False.
97     '''
98 
99     image_ratio = AliasProperty(get_image_ratio, None, bind=('texture', ))
100     '''Ratio of the image (width / float(height).
101 
102     :data:`image_ratio` is a :class:`~kivy.properties.AliasProperty`, and is
103     read-only.
104     '''
105 
106     color = ListProperty([1, 1, 1, 1])
107     '''Image color, in the format (r, g, b, a). This attribute can be used to
108     'tint' an image. Be careful, if the source image is not gray/white, the
109     color will not really work as expected.
110 
111     .. versionadded:: 1.0.6
112 
113     :data:`color` is a :class:`~kivy.properties.ListProperty`, default to [1, 1,
114     1, 1].
115     '''
116 
117     allow_stretch = BooleanProperty(False)
118     '''If True, the normalized image size will be maximized to fit in the image
119     box. Otherwise, if the box is too tall, the image will not be stretched more
120     than 1:1 pixels.
121 
122     .. versionadded:: 1.0.7
123 
124     :data:`allow_stretch` is a :class:`~kivy.properties.BooleanProperty`,
125     default to False
126     '''
127 
128     keep_ratio = BooleanProperty(True)
129     '''If False along with allow_stretch being True, the normalized image
130     size will be maximized to fit in the image box, disregarding the aspect
131     ratio of the image.
132     Otherwise, if the box is too tall, the image will not be stretched more
133     than 1:1 pixels.
134 
135     .. versionadded:: 1.0.8
136 
137     :data:`keep_ratio` is a :class:`~kivy.properties.BooleanProperty`,
138     default to True
139     '''
140 
141     keep_data = BooleanProperty(False)
142     '''If true the underlaying _coreimage have to keep the raw image data.
143     Useful to perform pixel based collision detection
144 
145     .. versionadded:: 1.3.0
146 
147     :data:`keep_data` is a :class:`~kivy.properties.BooleanProperty`, default
148     to False
149     '''
150 
151     anim_delay = NumericProperty(.25)
152     '''Delay of animation if the image is sequenced (like an animated gif).
153     If the anim_delay is set to -1, the animation will be stopped.
154 
155     .. versionadded:: 1.0.8
156 
157     :data:`anim_delay` is a :class:`~kivy.properties.NumericProperty`, default
158     to .25 (4 FPS)
159     '''
160 
161     def get_norm_image_size(self):
162         if not self.texture:
163             return self.size
164         ratio = self.image_ratio
165         w, h = self.size
166         tw, th = self.texture.size
167 
168         # ensure that the width is always maximized to the containter width
169         if self.allow_stretch:
170             if not self.keep_ratio:
171                 return w, h
172             iw = w
173         else:
174             iw = min(w, tw)
175         # calculate the appropriate height
176         ih = iw / ratio
177         # if the height is too higher, take the height of the container
178         # and calculate appropriate width. no need to test further. :)
179         if ih > h:
180             if self.allow_stretch:
181                 ih = h
182             else:
183                 ih = min(h, th)
184             iw = ih * ratio
185 
186         return iw, ih
187 
188     norm_image_size = AliasProperty(get_norm_image_size, None, bind=(
189         'texture', 'size', 'image_ratio', 'allow_stretch'))
190     '''Normalized image size within the widget box.
191 
192     This size will always be fit to the widget size, and will preserve the image
193     ratio.
194 
195     :data:`norm_image_size` is a :class:`~kivy.properties.AliasProperty`, and is
196     read-only.
197     '''
198 
199     def __init__(self, **kwargs):
200         self._coreimage = None
201         super(Image, self).__init__(**kwargs)
202         self.bind(source=self.texture_update,
203                   mipmap=self.texture_update)
204         if self.source:
205             self.texture_update()
206 
207     def texture_update(self, *largs):
208         if not self.source:
209             self.texture = None
210         else:
211             filename = resource_find(self.source)
212             if filename is None:
213                 return
214             mipmap = self.mipmap
215             if self._coreimage is not None:
216                 self._coreimage.unbind(on_texture=self._on_tex_change)
217             self._coreimage = ci = CoreImage(filename, mipmap=mipmap,
218                     anim_delay=self.anim_delay, keep_data=self.keep_data)
219             ci.bind(on_texture=self._on_tex_change)
220             self.texture = ci.texture
221 
222     def on_anim_delay(self, instance, value):
223         if self._coreimage is None:
224             return
225         self._coreimage.anim_delay = value
226         if value < 0:
227             self._coreimage.anim_reset(False)
228 
229     def on_texture(self, instance, value):
230         if value is not None:
231             self.texture_size = list(value.size)
232 
233     def _on_tex_change(self, *largs):
234         # update texture from core image
235         self.texture = self._coreimage.texture
236 
237     def reload(self):
238         '''Reload image from disk. This facilitates re-loading of
239         image from disk in case contents change.
240 
241         .. versionadded:: 1.3.0
242 
243         Usage::
244 
245             im = Image(source = '1.jpg')
246             # -- do something --
247             im.reload()
248             # image will be re-loaded from disk
249 
250         '''
251         self._coreimage.remove_from_cache()
252         olsource = self.source
253         self.source = ''
254         self.source = olsource
255 
256 
257 class AsyncImage(Image):
258     '''Asynchronous Image class. See module documentation for more information.
259     '''
260 
261     def __init__(self, **kwargs):
262         self._coreimage = None
263         super(AsyncImage, self).__init__(**kwargs)
264         self.unbind(source=self.texture_update,
265                     mipmap=self.texture_update)
266 
267     def on_source(self, instance, value):
268         if not value:
269             if self._coreimage is not None:
270                 self._coreimage.unbind(on_texture=self._on_tex_change)
271             self.texture = None
272             self._coreimage = None
273         else:
274             if not self.is_uri(value):
275                 value = resource_find(value)
276             self._coreimage = image = Loader.image(value)
277             image.bind(on_load=self.on_source_load)
278             image.bind(on_texture=self._on_tex_change)
279             self.texture = image.texture
280 
281     def on_source_load(self, value):
282         image = self._coreimage.image
283         if not image:
284             return
285         self.texture = image.texture
286 
287     def is_uri(self, filename):
288         proto = filename.split('://', 1)[0]
289         return proto in ('http', 'https', 'ftp', 'smb')
290 
291     def _on_tex_change(self, *largs):
292         if self._coreimage:
293             self.texture = self._coreimage.texture
294
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kivy/uix/image.py b/kivy/uix/image.py
--- a/kivy/uix/image.py
+++ b/kivy/uix/image.py
@@ -47,6 +47,7 @@
 from kivy.properties import StringProperty, ObjectProperty, ListProperty, \
         AliasProperty, BooleanProperty, NumericProperty
 from kivy.loader import Loader
+from kivy.logger import Logger
 
 
 class Image(Widget):
@@ -210,7 +211,8 @@
         else:
             filename = resource_find(self.source)
             if filename is None:
-                return
+                return Logger.error('Image: Error reading file {filename}'.
+                                    format(filename=self.source))
             mipmap = self.mipmap
             if self._coreimage is not None:
                 self._coreimage.unbind(on_texture=self._on_tex_change)

{"golden_diff": "diff --git a/kivy/uix/image.py b/kivy/uix/image.py\n--- a/kivy/uix/image.py\n+++ b/kivy/uix/image.py\n@@ -47,6 +47,7 @@\n from kivy.properties import StringProperty, ObjectProperty, ListProperty, \\\n         AliasProperty, BooleanProperty, NumericProperty\n from kivy.loader import Loader\n+from kivy.logger import Logger\n \n \n class Image(Widget):\n@@ -210,7 +211,8 @@\n         else:\n             filename = resource_find(self.source)\n             if filename is None:\n-                return\n+                return Logger.error('Image: Error reading file {filename}'.\n+                                    format(filename=self.source))\n             mipmap = self.mipmap\n             if self._coreimage is not None:\n                 self._coreimage.unbind(on_texture=self._on_tex_change)\n", "issue": "No console warning when setting Image.source = \"some/file/that/does/not/exist\"\nWhen you load a file into an Image widget by setting .source = \"some/file/name.png\" there is a message printed on the console:\n\n[DEBUG  ] [ImagePygame ] Load <data/tiles/dirt.png>\n\nIf you set .source = to a filename that does not exist, there is no feedback. No debug message is printed, and the Image widget remains blank.\n\n", "before_files": [{"content": "'''\nImage\n=====\n\nThe :class:`Image` widget is used to display an image::\n\n    wimg = Image(source='mylogo.png')\n\nAsynchronous Loading\n--------------------\n\nTo load an image asynchronously (for example from an external webserver), use\nthe :class:`AsyncImage` subclass::\n\n    aimg = AsyncImage(source='http://mywebsite.com/logo.png')\n\nAlignment\n---------\n\nBy default, the image is centered and fit inside the widget bounding box.\nIf you don't want that, you can inherit from Image and create your own style.\n\nFor example, if you want your image to take the same size of your widget, you\ncan do::\n\n    class FullImage(Image):\n        pass\n\nAnd in your kivy language file, you can do::\n\n    <FullImage>:\n        canvas:\n            Color:\n                rgb: (1, 1, 1)\n            Rectangle:\n                texture: self.texture\n                size: self.size\n                pos: self.pos\n\n'''\n\n__all__ = ('Image', 'AsyncImage')\n\nfrom kivy.uix.widget import Widget\nfrom kivy.core.image import Image as CoreImage\nfrom kivy.resources import resource_find\nfrom kivy.properties import StringProperty, ObjectProperty, ListProperty, \\\n        AliasProperty, BooleanProperty, NumericProperty\nfrom kivy.loader import Loader\n\n\nclass Image(Widget):\n    '''Image class, see module documentation for more information.\n    '''\n\n    source = StringProperty(None)\n    '''Filename / source of your image.\n\n    :data:`source` is a :class:`~kivy.properties.StringProperty`, default to\n    None.\n    '''\n\n    texture = ObjectProperty(None, allownone=True)\n    '''Texture object of the image.\n\n    Depending of the texture creation, the value will be a\n    :class:`~kivy.graphics.texture.Texture` or\n    :class:`~kivy.graphics.texture.TextureRegion` object.\n\n    :data:`texture` is a :class:`~kivy.properties.ObjectProperty`, default to\n    None.\n    '''\n\n    texture_size = ListProperty([0, 0])\n    '''Texture size of the image.\n\n    .. warning::\n\n        The texture size is set after the texture property. So if you listen on\n        the change to :data:`texture`, the property texture_size will not be\n        up-to-date. Use self.texture.size instead.\n    '''\n\n    def get_image_ratio(self):\n        if self.texture:\n            return self.texture.width / float(self.texture.height)\n        return 1.\n\n    mipmap = BooleanProperty(False)\n    '''Indicate if you want OpenGL mipmapping to be applied on the texture.\n    Read :ref:`mipmap` for more information.\n\n    .. versionadded:: 1.0.7\n\n    :data:`mipmap` is a :class:`~kivy.properties.BooleanProperty`, default to\n    False.\n    '''\n\n    image_ratio = AliasProperty(get_image_ratio, None, bind=('texture', ))\n    '''Ratio of the image (width / float(height).\n\n    :data:`image_ratio` is a :class:`~kivy.properties.AliasProperty`, and is\n    read-only.\n    '''\n\n    color = ListProperty([1, 1, 1, 1])\n    '''Image color, in the format (r, g, b, a). This attribute can be used to\n    'tint' an image. Be careful, if the source image is not gray/white, the\n    color will not really work as expected.\n\n    .. versionadded:: 1.0.6\n\n    :data:`color` is a :class:`~kivy.properties.ListProperty`, default to [1, 1,\n    1, 1].\n    '''\n\n    allow_stretch = BooleanProperty(False)\n    '''If True, the normalized image size will be maximized to fit in the image\n    box. Otherwise, if the box is too tall, the image will not be stretched more\n    than 1:1 pixels.\n\n    .. versionadded:: 1.0.7\n\n    :data:`allow_stretch` is a :class:`~kivy.properties.BooleanProperty`,\n    default to False\n    '''\n\n    keep_ratio = BooleanProperty(True)\n    '''If False along with allow_stretch being True, the normalized image\n    size will be maximized to fit in the image box, disregarding the aspect\n    ratio of the image.\n    Otherwise, if the box is too tall, the image will not be stretched more\n    than 1:1 pixels.\n\n    .. versionadded:: 1.0.8\n\n    :data:`keep_ratio` is a :class:`~kivy.properties.BooleanProperty`,\n    default to True\n    '''\n\n    keep_data = BooleanProperty(False)\n    '''If true the underlaying _coreimage have to keep the raw image data.\n    Useful to perform pixel based collision detection\n\n    .. versionadded:: 1.3.0\n\n    :data:`keep_data` is a :class:`~kivy.properties.BooleanProperty`, default\n    to False\n    '''\n\n    anim_delay = NumericProperty(.25)\n    '''Delay of animation if the image is sequenced (like an animated gif).\n    If the anim_delay is set to -1, the animation will be stopped.\n\n    .. versionadded:: 1.0.8\n\n    :data:`anim_delay` is a :class:`~kivy.properties.NumericProperty`, default\n    to .25 (4 FPS)\n    '''\n\n    def get_norm_image_size(self):\n        if not self.texture:\n            return self.size\n        ratio = self.image_ratio\n        w, h = self.size\n        tw, th = self.texture.size\n\n        # ensure that the width is always maximized to the containter width\n        if self.allow_stretch:\n            if not self.keep_ratio:\n                return w, h\n            iw = w\n        else:\n            iw = min(w, tw)\n        # calculate the appropriate height\n        ih = iw / ratio\n        # if the height is too higher, take the height of the container\n        # and calculate appropriate width. no need to test further. :)\n        if ih > h:\n            if self.allow_stretch:\n                ih = h\n            else:\n                ih = min(h, th)\n            iw = ih * ratio\n\n        return iw, ih\n\n    norm_image_size = AliasProperty(get_norm_image_size, None, bind=(\n        'texture', 'size', 'image_ratio', 'allow_stretch'))\n    '''Normalized image size within the widget box.\n\n    This size will always be fit to the widget size, and will preserve the image\n    ratio.\n\n    :data:`norm_image_size` is a :class:`~kivy.properties.AliasProperty`, and is\n    read-only.\n    '''\n\n    def __init__(self, **kwargs):\n        self._coreimage = None\n        super(Image, self).__init__(**kwargs)\n        self.bind(source=self.texture_update,\n                  mipmap=self.texture_update)\n        if self.source:\n            self.texture_update()\n\n    def texture_update(self, *largs):\n        if not self.source:\n            self.texture = None\n        else:\n            filename = resource_find(self.source)\n            if filename is None:\n                return\n            mipmap = self.mipmap\n            if self._coreimage is not None:\n                self._coreimage.unbind(on_texture=self._on_tex_change)\n            self._coreimage = ci = CoreImage(filename, mipmap=mipmap,\n                    anim_delay=self.anim_delay, keep_data=self.keep_data)\n            ci.bind(on_texture=self._on_tex_change)\n            self.texture = ci.texture\n\n    def on_anim_delay(self, instance, value):\n        if self._coreimage is None:\n            return\n        self._coreimage.anim_delay = value\n        if value < 0:\n            self._coreimage.anim_reset(False)\n\n    def on_texture(self, instance, value):\n        if value is not None:\n            self.texture_size = list(value.size)\n\n    def _on_tex_change(self, *largs):\n        # update texture from core image\n        self.texture = self._coreimage.texture\n\n    def reload(self):\n        '''Reload image from disk. This facilitates re-loading of\n        image from disk in case contents change.\n\n        .. versionadded:: 1.3.0\n\n        Usage::\n\n            im = Image(source = '1.jpg')\n            # -- do something --\n            im.reload()\n            # image will be re-loaded from disk\n\n        '''\n        self._coreimage.remove_from_cache()\n        olsource = self.source\n        self.source = ''\n        self.source = olsource\n\n\nclass AsyncImage(Image):\n    '''Asynchronous Image class. See module documentation for more information.\n    '''\n\n    def __init__(self, **kwargs):\n        self._coreimage = None\n        super(AsyncImage, self).__init__(**kwargs)\n        self.unbind(source=self.texture_update,\n                    mipmap=self.texture_update)\n\n    def on_source(self, instance, value):\n        if not value:\n            if self._coreimage is not None:\n                self._coreimage.unbind(on_texture=self._on_tex_change)\n            self.texture = None\n            self._coreimage = None\n        else:\n            if not self.is_uri(value):\n                value = resource_find(value)\n            self._coreimage = image = Loader.image(value)\n            image.bind(on_load=self.on_source_load)\n            image.bind(on_texture=self._on_tex_change)\n            self.texture = image.texture\n\n    def on_source_load(self, value):\n        image = self._coreimage.image\n        if not image:\n            return\n        self.texture = image.texture\n\n    def is_uri(self, filename):\n        proto = filename.split('://', 1)[0]\n        return proto in ('http', 'https', 'ftp', 'smb')\n\n    def _on_tex_change(self, *largs):\n        if self._coreimage:\n            self.texture = self._coreimage.texture\n", "path": "kivy/uix/image.py"}], "after_files": [{"content": "'''\nImage\n=====\n\nThe :class:`Image` widget is used to display an image::\n\n    wimg = Image(source='mylogo.png')\n\nAsynchronous Loading\n--------------------\n\nTo load an image asynchronously (for example from an external webserver), use\nthe :class:`AsyncImage` subclass::\n\n    aimg = AsyncImage(source='http://mywebsite.com/logo.png')\n\nAlignment\n---------\n\nBy default, the image is centered and fit inside the widget bounding box.\nIf you don't want that, you can inherit from Image and create your own style.\n\nFor example, if you want your image to take the same size of your widget, you\ncan do::\n\n    class FullImage(Image):\n        pass\n\nAnd in your kivy language file, you can do::\n\n    <FullImage>:\n        canvas:\n            Color:\n                rgb: (1, 1, 1)\n            Rectangle:\n                texture: self.texture\n                size: self.size\n                pos: self.pos\n\n'''\n\n__all__ = ('Image', 'AsyncImage')\n\nfrom kivy.uix.widget import Widget\nfrom kivy.core.image import Image as CoreImage\nfrom kivy.resources import resource_find\nfrom kivy.properties import StringProperty, ObjectProperty, ListProperty, \\\n        AliasProperty, BooleanProperty, NumericProperty\nfrom kivy.loader import Loader\nfrom kivy.logger import Logger\n\n\nclass Image(Widget):\n    '''Image class, see module documentation for more information.\n    '''\n\n    source = StringProperty(None)\n    '''Filename / source of your image.\n\n    :data:`source` is a :class:`~kivy.properties.StringProperty`, default to\n    None.\n    '''\n\n    texture = ObjectProperty(None, allownone=True)\n    '''Texture object of the image.\n\n    Depending of the texture creation, the value will be a\n    :class:`~kivy.graphics.texture.Texture` or\n    :class:`~kivy.graphics.texture.TextureRegion` object.\n\n    :data:`texture` is a :class:`~kivy.properties.ObjectProperty`, default to\n    None.\n    '''\n\n    texture_size = ListProperty([0, 0])\n    '''Texture size of the image.\n\n    .. warning::\n\n        The texture size is set after the texture property. So if you listen on\n        the change to :data:`texture`, the property texture_size will not be\n        up-to-date. Use self.texture.size instead.\n    '''\n\n    def get_image_ratio(self):\n        if self.texture:\n            return self.texture.width / float(self.texture.height)\n        return 1.\n\n    mipmap = BooleanProperty(False)\n    '''Indicate if you want OpenGL mipmapping to be applied on the texture.\n    Read :ref:`mipmap` for more information.\n\n    .. versionadded:: 1.0.7\n\n    :data:`mipmap` is a :class:`~kivy.properties.BooleanProperty`, default to\n    False.\n    '''\n\n    image_ratio = AliasProperty(get_image_ratio, None, bind=('texture', ))\n    '''Ratio of the image (width / float(height).\n\n    :data:`image_ratio` is a :class:`~kivy.properties.AliasProperty`, and is\n    read-only.\n    '''\n\n    color = ListProperty([1, 1, 1, 1])\n    '''Image color, in the format (r, g, b, a). This attribute can be used to\n    'tint' an image. Be careful, if the source image is not gray/white, the\n    color will not really work as expected.\n\n    .. versionadded:: 1.0.6\n\n    :data:`color` is a :class:`~kivy.properties.ListProperty`, default to [1, 1,\n    1, 1].\n    '''\n\n    allow_stretch = BooleanProperty(False)\n    '''If True, the normalized image size will be maximized to fit in the image\n    box. Otherwise, if the box is too tall, the image will not be stretched more\n    than 1:1 pixels.\n\n    .. versionadded:: 1.0.7\n\n    :data:`allow_stretch` is a :class:`~kivy.properties.BooleanProperty`,\n    default to False\n    '''\n\n    keep_ratio = BooleanProperty(True)\n    '''If False along with allow_stretch being True, the normalized image\n    size will be maximized to fit in the image box, disregarding the aspect\n    ratio of the image.\n    Otherwise, if the box is too tall, the image will not be stretched more\n    than 1:1 pixels.\n\n    .. versionadded:: 1.0.8\n\n    :data:`keep_ratio` is a :class:`~kivy.properties.BooleanProperty`,\n    default to True\n    '''\n\n    keep_data = BooleanProperty(False)\n    '''If true the underlaying _coreimage have to keep the raw image data.\n    Useful to perform pixel based collision detection\n\n    .. versionadded:: 1.3.0\n\n    :data:`keep_data` is a :class:`~kivy.properties.BooleanProperty`, default\n    to False\n    '''\n\n    anim_delay = NumericProperty(.25)\n    '''Delay of animation if the image is sequenced (like an animated gif).\n    If the anim_delay is set to -1, the animation will be stopped.\n\n    .. versionadded:: 1.0.8\n\n    :data:`anim_delay` is a :class:`~kivy.properties.NumericProperty`, default\n    to .25 (4 FPS)\n    '''\n\n    def get_norm_image_size(self):\n        if not self.texture:\n            return self.size\n        ratio = self.image_ratio\n        w, h = self.size\n        tw, th = self.texture.size\n\n        # ensure that the width is always maximized to the containter width\n        if self.allow_stretch:\n            if not self.keep_ratio:\n                return w, h\n            iw = w\n        else:\n            iw = min(w, tw)\n        # calculate the appropriate height\n        ih = iw / ratio\n        # if the height is too higher, take the height of the container\n        # and calculate appropriate width. no need to test further. :)\n        if ih > h:\n            if self.allow_stretch:\n                ih = h\n            else:\n                ih = min(h, th)\n            iw = ih * ratio\n\n        return iw, ih\n\n    norm_image_size = AliasProperty(get_norm_image_size, None, bind=(\n        'texture', 'size', 'image_ratio', 'allow_stretch'))\n    '''Normalized image size within the widget box.\n\n    This size will always be fit to the widget size, and will preserve the image\n    ratio.\n\n    :data:`norm_image_size` is a :class:`~kivy.properties.AliasProperty`, and is\n    read-only.\n    '''\n\n    def __init__(self, **kwargs):\n        self._coreimage = None\n        super(Image, self).__init__(**kwargs)\n        self.bind(source=self.texture_update,\n                  mipmap=self.texture_update)\n        if self.source:\n            self.texture_update()\n\n    def texture_update(self, *largs):\n        if not self.source:\n            self.texture = None\n        else:\n            filename = resource_find(self.source)\n            if filename is None:\n                return Logger.error('Image: Error reading file {filename}'.\n                                    format(filename=self.source))\n            mipmap = self.mipmap\n            if self._coreimage is not None:\n                self._coreimage.unbind(on_texture=self._on_tex_change)\n            self._coreimage = ci = CoreImage(filename, mipmap=mipmap,\n                    anim_delay=self.anim_delay, keep_data=self.keep_data)\n            ci.bind(on_texture=self._on_tex_change)\n            self.texture = ci.texture\n\n    def on_anim_delay(self, instance, value):\n        if self._coreimage is None:\n            return\n        self._coreimage.anim_delay = value\n        if value < 0:\n            self._coreimage.anim_reset(False)\n\n    def on_texture(self, instance, value):\n        if value is not None:\n            self.texture_size = list(value.size)\n\n    def _on_tex_change(self, *largs):\n        # update texture from core image\n        self.texture = self._coreimage.texture\n\n    def reload(self):\n        '''Reload image from disk. This facilitates re-loading of\n        image from disk in case contents change.\n\n        .. versionadded:: 1.3.0\n\n        Usage::\n\n            im = Image(source = '1.jpg')\n            # -- do something --\n            im.reload()\n            # image will be re-loaded from disk\n\n        '''\n        self._coreimage.remove_from_cache()\n        olsource = self.source\n        self.source = ''\n        self.source = olsource\n\n\nclass AsyncImage(Image):\n    '''Asynchronous Image class. See module documentation for more information.\n    '''\n\n    def __init__(self, **kwargs):\n        self._coreimage = None\n        super(AsyncImage, self).__init__(**kwargs)\n        self.unbind(source=self.texture_update,\n                    mipmap=self.texture_update)\n\n    def on_source(self, instance, value):\n        if not value:\n            if self._coreimage is not None:\n                self._coreimage.unbind(on_texture=self._on_tex_change)\n            self.texture = None\n            self._coreimage = None\n        else:\n            if not self.is_uri(value):\n                value = resource_find(value)\n            self._coreimage = image = Loader.image(value)\n            image.bind(on_load=self.on_source_load)\n            image.bind(on_texture=self._on_tex_change)\n            self.texture = image.texture\n\n    def on_source_load(self, value):\n        image = self._coreimage.image\n        if not image:\n            return\n        self.texture = image.texture\n\n    def is_uri(self, filename):\n        proto = filename.split('://', 1)[0]\n        return proto in ('http', 'https', 'ftp', 'smb')\n\n    def _on_tex_change(self, *largs):\n        if self._coreimage:\n            self.texture = self._coreimage.texture\n", "path": "kivy/uix/image.py"}]}