Coverage for app/venv/lib/python3.14/site-packages/weblate/machinery/models.py: 45%

34 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 07:15 +0000

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

7import json 

8from typing import TYPE_CHECKING, cast 

9 

10from appconf import AppConf 

11 

12from weblate.utils.classloader import ClassLoader 

13 

14from .base import BatchMachineTranslation 

15 

16if TYPE_CHECKING: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true

17 from .types import SettingsDict 

18 

19MACHINERY = ClassLoader( 

20 "WEBLATE_MACHINERY", 

21 construct=False, 

22 collect_errors=True, 

23 base_class=BatchMachineTranslation, 

24) 

25 

26 

27class WeblateConf(AppConf): 

28 """Machine-translation settings.""" 

29 

30 # List of machinery classes 

31 WEBLATE_MACHINERY = ( 

32 "weblate.machinery.apertium.ApertiumAPYTranslation", 

33 "weblate.machinery.aws.AWSTranslation", 

34 "weblate.machinery.alibaba.AlibabaTranslation", 

35 "weblate.machinery.baidu.BaiduTranslation", 

36 "weblate.machinery.deepl.DeepLTranslation", 

37 "weblate.machinery.glosbe.GlosbeTranslation", 

38 "weblate.machinery.google.GoogleTranslation", 

39 "weblate.machinery.googlev3.GoogleV3Translation", 

40 "weblate.machinery.libretranslate.LibreTranslateTranslation", 

41 "weblate.machinery.microsoft.MicrosoftCognitiveTranslation", 

42 "weblate.machinery.modernmt.ModernMTTranslation", 

43 "weblate.machinery.mymemory.MyMemoryTranslation", 

44 "weblate.machinery.netease.NeteaseSightTranslation", 

45 "weblate.machinery.tmserver.TMServerTranslation", 

46 "weblate.machinery.yandex.YandexTranslation", 

47 "weblate.machinery.yandexv2.YandexV2Translation", 

48 "weblate.machinery.saptranslationhub.SAPTranslationHub", 

49 "weblate.machinery.youdao.YoudaoTranslation", 

50 "weblate.machinery.systran.SystranTranslation", 

51 "weblate.machinery.openai.OpenAITranslation", 

52 "weblate.machinery.openai.AzureOpenAITranslation", 

53 "weblate.machinery.weblatetm.WeblateTranslation", 

54 "weblate.memory.machine.WeblateMemory", 

55 "weblate.machinery.cyrtranslit.CyrTranslitTranslation", 

56 ) 

57 

58 class Meta: 

59 prefix = "" 

60 

61 

62def validate_service_configuration( 

63 service_name: str, 

64 configuration: str | SettingsDict, 

65) -> tuple[BatchMachineTranslation | None, SettingsDict, list[str]]: 

66 """ 

67 Validate given service configuration. 

68 

69 :param service_name: Name of the service as defined in WEBLATE_MACHINERY 

70 :param configuration: JSON encoded configuration for the service 

71 :return: A tuple containing the validated service class, configuration 

72 and a list of errors 

73 :raises ValueError: When service is not found or configuration is invalid 

74 """ 

75 try: 

76 service = MACHINERY[service_name] 

77 except KeyError: 

78 msg = f"Service not found: {service_name}" 

79 return None, {}, [msg] 

80 

81 service_configuration: SettingsDict 

82 

83 if isinstance(configuration, str): 

84 try: 

85 service_configuration = cast("SettingsDict", json.loads(configuration)) 

86 except json.JSONDecodeError as error: 

87 msg = f"Invalid service configuration ({service_name}): {error}" 

88 return service, {}, [msg] 

89 else: 

90 service_configuration = configuration 

91 

92 errors = [] 

93 if service.settings_form is not None: 

94 form = service.settings_form(service, data=service_configuration) 

95 # validate form 

96 if not form.is_valid(): 

97 errors.extend([str(error) for error in form.non_field_errors()]) 

98 

99 for field in form: 

100 errors.extend( 

101 [ 

102 f"Error in {field.name} ({service_name}): {error}" 

103 for error in field.errors 

104 ] 

105 ) 

106 return service, service_configuration, errors