@@ -117,7 +117,7 @@ def _api_call(
117
117
stream : bool = False ,
118
118
headers : Optional [dict ] = None ,
119
119
** kwargs ,
120
- ) -> Tuple [int , Union [str , requests .Response ], dict ]:
120
+ ) -> Tuple [int , Union [str , requests .Response ], Any ]:
121
121
"""
122
122
Makes a request to the API, and returns response as status code,
123
123
content and JSON object.
@@ -167,14 +167,14 @@ def _raise_for_status(
167
167
self ,
168
168
status_code : int ,
169
169
content : Union [str , requests .Response ],
170
- json : Optional [ dict ] ,
170
+ json : Any ,
171
171
glossary : bool = False ,
172
172
downloading_document : bool = False ,
173
173
):
174
174
message = ""
175
- if json is not None and "message" in json :
175
+ if json is not None and isinstance ( json , dict ) and "message" in json :
176
176
message += ", message: " + json ["message" ]
177
- if json is not None and "detail" in json :
177
+ if json is not None and isinstance ( json , dict ) and "detail" in json :
178
178
message += ", detail: " + json ["detail" ]
179
179
180
180
if 200 <= status_code < 400 :
@@ -296,7 +296,7 @@ def _create_glossary(
296
296
source_lang : Union [str , Language ],
297
297
target_lang : Union [str , Language ],
298
298
entries_format : str ,
299
- entries : str ,
299
+ entries : Union [ str , bytes ] ,
300
300
) -> GlossaryInfo :
301
301
# glossaries are only supported for base language types
302
302
source_lang = Language .remove_regional_variant (source_lang )
@@ -437,11 +437,19 @@ def join_tags(tag_argument: Union[str, Iterable[str]]) -> List[str]:
437
437
438
438
self ._raise_for_status (status , content , json )
439
439
440
- translations = json .get ("translations" , [])
440
+ translations = (
441
+ json .get ("translations" , [])
442
+ if (json and isinstance (json , dict ))
443
+ else []
444
+ )
441
445
output = []
442
446
for translation in translations :
443
- text = translation .get ("text" )
444
- lang = translation .get ("detected_source_language" )
447
+ text = translation .get ("text" , "" ) if translation else ""
448
+ lang = (
449
+ translation .get ("detected_source_language" , "" )
450
+ if translation
451
+ else ""
452
+ )
445
453
output .append (TextResult (text , detected_source_lang = lang ))
446
454
447
455
return output if multi_input else output [0 ]
@@ -633,6 +641,7 @@ def translate_document_upload(
633
641
source_lang , target_lang , formality , glossary
634
642
)
635
643
644
+ files : Dict [str , Any ] = {}
636
645
if isinstance (input_document , (str , bytes )):
637
646
if filename is None :
638
647
raise ValueError (
@@ -647,7 +656,11 @@ def translate_document_upload(
647
656
)
648
657
self ._raise_for_status (status , content , json )
649
658
650
- return DocumentHandle (json ["document_id" ], json ["document_key" ])
659
+ if not json :
660
+ json = {}
661
+ return DocumentHandle (
662
+ json .get ("document_id" , "" ), json .get ("document_key" , "" )
663
+ )
651
664
652
665
def translate_document_get_status (
653
666
self , handle : DocumentHandle
@@ -657,19 +670,42 @@ def translate_document_get_status(
657
670
658
671
:param handle: DocumentHandle to the request to check.
659
672
:return: DocumentStatus containing the request status.
673
+
674
+ :raises DocumentTranslationException: If an error occurs during
675
+ querying the document, the exception includes the document handle.
660
676
"""
661
677
662
678
data = {"document_key" : handle .document_key }
663
679
url = f"v2/document/{ handle .document_id } "
664
680
665
- status , content , json = self ._api_call (url , json = data )
681
+ status_code , content , json = self ._api_call (url , json = data )
666
682
667
- self ._raise_for_status (status , content , json )
683
+ self ._raise_for_status (status_code , content , json )
668
684
669
- status = json ["status" ]
670
- seconds_remaining = json .get ("seconds_remaining" , None )
671
- billed_characters = json .get ("billed_characters" , None )
672
- error_message = json .get ("error_message" , None )
685
+ status = (
686
+ json .get ("status" , None )
687
+ if (json and isinstance (json , dict ))
688
+ else None
689
+ )
690
+ if not status :
691
+ raise DocumentTranslationException (
692
+ "Querying document status gave an empty response" , handle
693
+ )
694
+ seconds_remaining = (
695
+ json .get ("seconds_remaining" , None )
696
+ if (json and isinstance (json , dict ))
697
+ else None
698
+ )
699
+ billed_characters = (
700
+ json .get ("billed_characters" , None )
701
+ if (json and isinstance (json , dict ))
702
+ else None
703
+ )
704
+ error_message = (
705
+ json .get ("error_message" , None )
706
+ if (json and isinstance (json , dict ))
707
+ else None
708
+ )
673
709
return DocumentStatus (
674
710
status , seconds_remaining , billed_characters , error_message
675
711
)
@@ -726,17 +762,17 @@ def translate_document_download(
726
762
status_code , response , json = self ._api_call (
727
763
url , json = data , stream = True
728
764
)
765
+ # TODO: once we drop py3.6 support, replace this with @overload
766
+ # annotations in `_api_call` and chained private functions.
767
+ # See for example https://stackoverflow.com/a/74070166/4926599
768
+ assert isinstance (response , requests .Response )
729
769
730
770
self ._raise_for_status (
731
771
status_code , "<file>" , json , downloading_document = True
732
772
)
733
773
734
774
if output_file :
735
- chunks = (
736
- response .iter_content (chunk_size = chunk_size )
737
- if isinstance (response , requests .Response )
738
- else [response ]
739
- )
775
+ chunks = response .iter_content (chunk_size = chunk_size )
740
776
for chunk in chunks :
741
777
output_file .write (chunk )
742
778
return None
@@ -753,12 +789,13 @@ def get_source_languages(self, skip_cache=False) -> List[Language]:
753
789
"""
754
790
status , content , json = self ._api_call ("v2/languages" , method = "GET" )
755
791
self ._raise_for_status (status , content , json )
792
+ languages = json if (json and isinstance (json , list )) else []
756
793
return [
757
794
Language (
758
795
language ["language" ],
759
796
language ["name" ],
760
797
)
761
- for language in json
798
+ for language in languages
762
799
]
763
800
764
801
def get_target_languages (self , skip_cache = False ) -> List [Language ]:
@@ -774,13 +811,14 @@ def get_target_languages(self, skip_cache=False) -> List[Language]:
774
811
"v2/languages" , method = "GET" , data = data
775
812
)
776
813
self ._raise_for_status (status , content , json )
814
+ languages = json if (json and isinstance (json , list )) else []
777
815
return [
778
816
Language (
779
817
language ["language" ],
780
818
language ["name" ],
781
819
language .get ("supports_formality" , None ),
782
820
)
783
- for language in json
821
+ for language in languages
784
822
]
785
823
786
824
def get_glossary_languages (self ) -> List [GlossaryLanguagePair ]:
@@ -791,11 +829,16 @@ def get_glossary_languages(self) -> List[GlossaryLanguagePair]:
791
829
792
830
self ._raise_for_status (status , content , json )
793
831
832
+ supported_languages = (
833
+ json .get ("supported_languages" , [])
834
+ if (json and isinstance (json , dict ))
835
+ else []
836
+ )
794
837
return [
795
838
GlossaryLanguagePair (
796
839
language_pair ["source_lang" ], language_pair ["target_lang" ]
797
840
)
798
- for language_pair in json [ " supported_languages" ]
841
+ for language_pair in supported_languages
799
842
]
800
843
801
844
def get_usage (self ) -> Usage :
@@ -804,6 +847,8 @@ def get_usage(self) -> Usage:
804
847
805
848
self ._raise_for_status (status , content , json )
806
849
850
+ if not isinstance (json , dict ):
851
+ json = {}
807
852
return Usage (json )
808
853
809
854
def create_glossary (
@@ -888,6 +933,8 @@ def create_glossary_from_csv(
888
933
csv_data if isinstance (csv_data , (str , bytes )) else csv_data .read ()
889
934
)
890
935
936
+ if not isinstance (entries , (bytes , str )):
937
+ raise ValueError ("Entries of the glossary are invalid" )
891
938
return self ._create_glossary (
892
939
name , source_lang , target_lang , "csv" , entries
893
940
)
@@ -913,9 +960,12 @@ def list_glossaries(self) -> List[GlossaryInfo]:
913
960
"""
914
961
status , content , json = self ._api_call ("v2/glossaries" , method = "GET" )
915
962
self ._raise_for_status (status , content , json , glossary = True )
916
- return [
917
- GlossaryInfo .from_json (glossary ) for glossary in json ["glossaries" ]
918
- ]
963
+ glossaries = (
964
+ json .get ("glossaries" , [])
965
+ if (json and isinstance (json , dict ))
966
+ else []
967
+ )
968
+ return [GlossaryInfo .from_json (glossary ) for glossary in glossaries ]
919
969
920
970
def get_glossary_entries (self , glossary : Union [str , GlossaryInfo ]) -> dict :
921
971
"""Retrieves the entries of the specified glossary and returns them as
@@ -925,6 +975,8 @@ def get_glossary_entries(self, glossary: Union[str, GlossaryInfo]) -> dict:
925
975
:return: dictionary of glossary entries.
926
976
:raises GlossaryNotFoundException: If no glossary with given ID is
927
977
found.
978
+ :raises DeepLException: If the glossary could not be retrieved
979
+ in the right format.
928
980
"""
929
981
if isinstance (glossary , GlossaryInfo ):
930
982
glossary_id = glossary .glossary_id
@@ -937,6 +989,11 @@ def get_glossary_entries(self, glossary: Union[str, GlossaryInfo]) -> dict:
937
989
headers = {"Accept" : "text/tab-separated-values" },
938
990
)
939
991
self ._raise_for_status (status , content , json , glossary = True )
992
+ if not isinstance (content , str ):
993
+ raise DeepLException (
994
+ "Could not get the glossary content as a string" ,
995
+ http_status_code = status ,
996
+ )
940
997
return util .convert_tsv_to_dict (content )
941
998
942
999
def delete_glossary (self , glossary : Union [str , GlossaryInfo ]) -> None :
0 commit comments