@@ -120,43 +120,50 @@ def udf(self):
120120
121121
122122def exif_func (src_obj_ref_rt : str , verbose : bool ) -> str :
123- import io
124- import json
123+ try :
124+ import io
125+ import json
125126
126- from PIL import ExifTags , Image
127- import requests
128- from requests import adapters
127+ from PIL import ExifTags , Image
128+ import requests
129+ from requests import adapters
129130
130- result_dict = {"status" : "" , "content" : "{}" }
131- try :
132131 session = requests .Session ()
133132 session .mount ("https://" , adapters .HTTPAdapter (max_retries = 3 ))
134133
135134 src_obj_ref_rt_json = json .loads (src_obj_ref_rt )
136-
137135 src_url = src_obj_ref_rt_json ["access_urls" ]["read_url" ]
138136
139137 response = session .get (src_url , timeout = 30 )
138+ response .raise_for_status ()
140139 bts = response .content
141140
142141 image = Image .open (io .BytesIO (bts ))
143142 exif_data = image .getexif ()
144143 exif_dict = {}
144+
145145 if exif_data :
146146 for tag , value in exif_data .items ():
147147 tag_name = ExifTags .TAGS .get (tag , tag )
148- # Pillow might return bytes, which are not serializable.
149- if isinstance (value , bytes ):
150- value = value .decode ("utf-8" , "replace" )
151- exif_dict [tag_name ] = value
152- result_dict ["content" ] = json .dumps (exif_dict )
153- except Exception as e :
154- result_dict ["status" ] = str (e )
148+ # Convert non-serializable types to strings
149+ try :
150+ json .dumps (value )
151+ exif_dict [tag_name ] = value
152+ except (TypeError , ValueError ):
153+ exif_dict [tag_name ] = str (value )
154+
155+ if verbose :
156+ return json .dumps ({"status" : "" , "content" : json .dumps (exif_dict )})
157+ else :
158+ return json .dumps (exif_dict )
155159
156- if verbose :
157- return json .dumps (result_dict )
158- else :
159- return result_dict ["content" ]
160+ except Exception as e :
161+ # Return error as JSON with error field
162+ error_result = {"status" : f"{ type (e ).__name__ } : { str (e )} " , "content" : "{}" }
163+ if verbose :
164+ return json .dumps (error_result )
165+ else :
166+ return "{}"
160167
161168
162169exif_func_def = FunctionDef (exif_func , ["pillow" , "requests" ])
@@ -171,11 +178,9 @@ def image_blur_func(
171178 ext : str ,
172179 verbose : bool ,
173180) -> str :
174- import json
175-
176- result_dict = {"status" : "" , "content" : dst_obj_ref_rt }
177-
178181 try :
182+ import json
183+
179184 import cv2 as cv # type: ignore
180185 import numpy as np
181186 import requests
@@ -193,35 +198,52 @@ def image_blur_func(
193198 dst_url = dst_obj_ref_rt_json ["access_urls" ]["write_url" ]
194199
195200 response = session .get (src_url , timeout = 30 )
201+ response .raise_for_status () # Raise exception for HTTP errors
196202 bts = response .content
197203
198204 nparr = np .frombuffer (bts , np .uint8 )
199205 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
206+
207+ if img is None :
208+ raise ValueError (
209+ "Failed to decode image - possibly corrupted or unsupported format"
210+ )
211+
200212 img_blurred = cv .blur (img , ksize = (ksize_x , ksize_y ))
201213
202- bts = cv .imencode (ext , img_blurred )[1 ].tobytes ()
214+ success , encoded = cv .imencode (ext , img_blurred )
215+ if not success :
216+ raise ValueError (f"Failed to encode image with extension { ext } " )
217+
218+ bts = encoded .tobytes ()
203219
204220 ext = ext .replace ("." , "" )
205221 ext_mappings = {"jpg" : "jpeg" , "tif" : "tiff" }
206222 ext = ext_mappings .get (ext , ext )
207223 content_type = "image/" + ext
208224
209- session .put (
225+ put_response = session .put (
210226 url = dst_url ,
211227 data = bts ,
212- headers = {
213- "Content-Type" : content_type ,
214- },
228+ headers = {"Content-Type" : content_type },
215229 timeout = 30 ,
216230 )
231+ put_response .raise_for_status ()
217232
218- except Exception as e :
219- result_dict ["status" ] = str (e )
233+ if verbose :
234+ return json .dumps ({"status" : "" , "content" : dst_obj_ref_rt })
235+ else :
236+ return dst_obj_ref_rt
220237
221- if verbose :
222- return json .dumps (result_dict )
223- else :
224- return result_dict ["content" ]
238+ except Exception as e :
239+ # Return error in structured format
240+ error_result = {"status" : f"Error: { type (e ).__name__ } : { str (e )} " , "content" : "" }
241+ if verbose :
242+ return json .dumps (error_result )
243+ else :
244+ # The calling function expects a json string that can be parsed as a blob ref
245+ # Return a valid blob ref json string with empty values.
246+ return '{"access_urls": {"read_url": "", "write_url": ""}, "authorizer": "", "generation": "", "uri": ""}'
225247
226248
227249image_blur_def = FunctionDef (image_blur_func , ["opencv-python" , "numpy" , "requests" ])
@@ -251,12 +273,20 @@ def image_blur_to_bytes_func(
251273 src_url = src_obj_ref_rt_json ["access_urls" ]["read_url" ]
252274
253275 response = session .get (src_url , timeout = 30 )
276+ response .raise_for_status ()
254277 bts = response .content
255278
256279 nparr = np .frombuffer (bts , np .uint8 )
257280 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
281+ if img is None :
282+ raise ValueError (
283+ "Failed to decode image - possibly corrupted or unsupported format"
284+ )
258285 img_blurred = cv .blur (img , ksize = (ksize_x , ksize_y ))
259- content = cv .imencode (ext , img_blurred )[1 ].tobytes ()
286+ success , encoded = cv .imencode (ext , img_blurred )
287+ if not success :
288+ raise ValueError (f"Failed to encode image with extension { ext } " )
289+ content = encoded .tobytes ()
260290
261291 except Exception as e :
262292 status = str (e )
@@ -284,11 +314,9 @@ def image_resize_func(
284314 ext : str ,
285315 verbose : bool ,
286316) -> str :
287- import json
288-
289- result_dict = {"status" : "" , "content" : dst_obj_ref_rt }
290-
291317 try :
318+ import json
319+
292320 import cv2 as cv # type: ignore
293321 import numpy as np
294322 import requests
@@ -306,35 +334,51 @@ def image_resize_func(
306334 dst_url = dst_obj_ref_rt_json ["access_urls" ]["write_url" ]
307335
308336 response = session .get (src_url , timeout = 30 )
337+ response .raise_for_status ()
309338 bts = response .content
310339
311340 nparr = np .frombuffer (bts , np .uint8 )
312341 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
342+ if img is None :
343+ raise ValueError (
344+ "Failed to decode image - possibly corrupted or unsupported format"
345+ )
313346 img_resized = cv .resize (img , dsize = (dsize_x , dsize_y ), fx = fx , fy = fy )
314347
315- bts = cv .imencode (ext , img_resized )[1 ].tobytes ()
348+ success , encoded = cv .imencode (ext , img_resized )
349+ if not success :
350+ raise ValueError (f"Failed to encode image with extension { ext } " )
351+ bts = encoded .tobytes ()
316352
317353 ext = ext .replace ("." , "" )
318354 ext_mappings = {"jpg" : "jpeg" , "tif" : "tiff" }
319355 ext = ext_mappings .get (ext , ext )
320356 content_type = "image/" + ext
321357
322- session .put (
358+ put_response = session .put (
323359 url = dst_url ,
324360 data = bts ,
325361 headers = {
326362 "Content-Type" : content_type ,
327363 },
328364 timeout = 30 ,
329365 )
366+ put_response .raise_for_status ()
330367
331- except Exception as e :
332- result_dict ["status" ] = str (e )
368+ if verbose :
369+ return json .dumps ({"status" : "" , "content" : dst_obj_ref_rt })
370+ else :
371+ return dst_obj_ref_rt
333372
334- if verbose :
335- return json .dumps (result_dict )
336- else :
337- return result_dict ["content" ]
373+ except Exception as e :
374+ # Return error in structured format
375+ error_result = {"status" : f"Error: { type (e ).__name__ } : { str (e )} " , "content" : "" }
376+ if verbose :
377+ return json .dumps (error_result )
378+ else :
379+ # The calling function expects a json string that can be parsed as a blob ref
380+ # Return a valid blob ref json string with empty values.
381+ return '{"access_urls": {"read_url": "", "write_url": ""}, "authorizer": "", "generation": "", "uri": ""}'
338382
339383
340384image_resize_def = FunctionDef (
@@ -372,12 +416,20 @@ def image_resize_to_bytes_func(
372416 src_url = src_obj_ref_rt_json ["access_urls" ]["read_url" ]
373417
374418 response = session .get (src_url , timeout = 30 )
419+ response .raise_for_status ()
375420 bts = response .content
376421
377422 nparr = np .frombuffer (bts , np .uint8 )
378423 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
424+ if img is None :
425+ raise ValueError (
426+ "Failed to decode image - possibly corrupted or unsupported format"
427+ )
379428 img_resized = cv .resize (img , dsize = (dsize_x , dsize_y ), fx = fx , fy = fy )
380- content = cv .imencode (".jpeg" , img_resized )[1 ].tobytes ()
429+ success , encoded = cv .imencode (ext , img_resized )
430+ if not success :
431+ raise ValueError (f"Failed to encode image with extension { ext } " )
432+ content = encoded .tobytes ()
381433
382434 except Exception as e :
383435 status = str (e )
@@ -404,11 +456,9 @@ def image_normalize_func(
404456 ext : str ,
405457 verbose : bool ,
406458) -> str :
407- import json
408-
409- result_dict = {"status" : "" , "content" : dst_obj_ref_rt }
410-
411459 try :
460+ import json
461+
412462 import cv2 as cv # type: ignore
413463 import numpy as np
414464 import requests
@@ -433,37 +483,53 @@ def image_normalize_func(
433483 dst_url = dst_obj_ref_rt_json ["access_urls" ]["write_url" ]
434484
435485 response = session .get (src_url , timeout = 30 )
486+ response .raise_for_status ()
436487 bts = response .content
437488
438489 nparr = np .frombuffer (bts , np .uint8 )
439490 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
491+ if img is None :
492+ raise ValueError (
493+ "Failed to decode image - possibly corrupted or unsupported format"
494+ )
440495 img_normalized = cv .normalize (
441496 img , None , alpha = alpha , beta = beta , norm_type = norm_type_mapping [norm_type ]
442497 )
443498
444- bts = cv .imencode (ext , img_normalized )[1 ].tobytes ()
499+ success , encoded = cv .imencode (ext , img_normalized )
500+ if not success :
501+ raise ValueError (f"Failed to encode image with extension { ext } " )
502+ bts = encoded .tobytes ()
445503
446504 ext = ext .replace ("." , "" )
447505 ext_mappings = {"jpg" : "jpeg" , "tif" : "tiff" }
448506 ext = ext_mappings .get (ext , ext )
449507 content_type = "image/" + ext
450508
451- session .put (
509+ put_response = session .put (
452510 url = dst_url ,
453511 data = bts ,
454512 headers = {
455513 "Content-Type" : content_type ,
456514 },
457515 timeout = 30 ,
458516 )
517+ put_response .raise_for_status ()
459518
460- except Exception as e :
461- result_dict ["status" ] = str (e )
519+ if verbose :
520+ return json .dumps ({"status" : "" , "content" : dst_obj_ref_rt })
521+ else :
522+ return dst_obj_ref_rt
462523
463- if verbose :
464- return json .dumps (result_dict )
465- else :
466- return result_dict ["content" ]
524+ except Exception as e :
525+ # Return error in structured format
526+ error_result = {"status" : f"Error: { type (e ).__name__ } : { str (e )} " , "content" : "" }
527+ if verbose :
528+ return json .dumps (error_result )
529+ else :
530+ # The calling function expects a json string that can be parsed as a blob ref
531+ # Return a valid blob ref json string with empty values.
532+ return '{"access_urls": {"read_url": "", "write_url": ""}, "authorizer": "", "generation": "", "uri": ""}'
467533
468534
469535image_normalize_def = FunctionDef (
@@ -482,7 +548,8 @@ def image_normalize_to_bytes_func(
482548 import base64
483549 import json
484550
485- result_dict = {"status" : "" , "content" : "" }
551+ status = ""
552+ content = b""
486553
487554 try :
488555 import cv2 as cv # type: ignore
@@ -506,20 +573,28 @@ def image_normalize_to_bytes_func(
506573 src_url = src_obj_ref_rt_json ["access_urls" ]["read_url" ]
507574
508575 response = session .get (src_url , timeout = 30 )
576+ response .raise_for_status ()
509577 bts = response .content
510578
511579 nparr = np .frombuffer (bts , np .uint8 )
512580 img = cv .imdecode (nparr , cv .IMREAD_UNCHANGED )
581+ if img is None :
582+ raise ValueError (
583+ "Failed to decode image - possibly corrupted or unsupported format"
584+ )
513585 img_normalized = cv .normalize (
514586 img , None , alpha = alpha , beta = beta , norm_type = norm_type_mapping [norm_type ]
515587 )
516- bts = cv .imencode (".jpeg" , img_normalized )[ 1 ]. tobytes ( )
517-
518- content_b64 = base64 . b64encode ( bts ). decode ( "utf-8 " )
519- result_dict [ " content" ] = content_b64
588+ success , encoded = cv .imencode (ext , img_normalized )
589+ if not success :
590+ raise ValueError ( f"Failed to encode image with extension { ext } " )
591+ content = encoded . tobytes ()
520592
521593 except Exception as e :
522- result_dict ["status" ] = str (e )
594+ status = str (e )
595+
596+ encoded_content = base64 .b64encode (content ).decode ("utf-8" )
597+ result_dict = {"status" : status , "content" : encoded_content }
523598
524599 if verbose :
525600 return json .dumps (result_dict )
0 commit comments