Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/docutils/languages/__init__.py: 62%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

68 statements  

1# $Id$ 

2# Author: David Goodger <goodger@python.org> 

3# Copyright: This module has been placed in the public domain. 

4 

5# Internationalization details are documented in 

6# <https://docutils.sourceforge.io/docs/howto/i18n.html>. 

7 

8""" 

9This package contains modules for language-dependent features of Docutils. 

10""" 

11 

12from __future__ import annotations 

13 

14__docformat__ = 'reStructuredText' 

15 

16from importlib import import_module 

17 

18from docutils.utils import normalize_language_tag 

19 

20TYPE_CHECKING = False 

21if TYPE_CHECKING: 

22 import types 

23 from typing import NoReturn, Protocol, TypeVar, overload 

24 

25 from docutils.utils import Reporter 

26 

27 class LanguageModule(Protocol): 

28 __name__: str 

29 

30 labels: dict[str, str] 

31 bibliographic_fields: dict[str, str] 

32 author_separators: list[str] 

33 

34 LanguageModuleT = TypeVar('LanguageModuleT') 

35else: 

36 from docutils.utils._typing import overload 

37 

38 

39class LanguageImporter: 

40 """Import language modules. 

41 

42 When called with a BCP 47 language tag, instances return a module 

43 with localisations from `docutils.languages` or the PYTHONPATH. 

44 

45 If there is no matching module, warn (if a `reporter` is passed) 

46 and fall back to English. 

47 """ 

48 packages = ('docutils.languages.', '') 

49 warn_msg = ('Language "%s" not supported: ' 

50 'Docutils-generated text will be in English.') 

51 fallback = 'en' 

52 # TODO: use a dummy module returning empty strings?, configurable? 

53 

54 def __init__(self) -> None: 

55 self.cache: dict[str, LanguageModuleT] = {} 

56 

57 def import_from_packages(self, name: str, reporter: Reporter = None 

58 ) -> LanguageModuleT: 

59 """Try loading module `name` from `self.packages`.""" 

60 module = None 

61 for package in self.packages: 

62 try: 

63 module = import_module(package + name) 

64 self.check_content(module) 

65 except (ImportError, AttributeError): 

66 if reporter and module: 

67 reporter.info(f'{module} is no complete ' 

68 'Docutils language module.') 

69 elif reporter: 

70 reporter.info(f'Module "{package+name}" not found.') 

71 continue 

72 break 

73 return module 

74 

75 @overload 

76 def check_content(self, module: LanguageModule) -> None: 

77 ... 

78 

79 @overload 

80 def check_content(self, module: types.ModuleType) -> NoReturn: 

81 ... 

82 

83 def check_content(self, module: LanguageModule | types.ModuleType) -> None: 

84 """Check if we got a Docutils language module.""" 

85 if not ( 

86 isinstance(module.labels, dict) 

87 and isinstance(module.bibliographic_fields, dict) 

88 and isinstance(module.author_separators, list) 

89 ): 

90 raise ImportError 

91 

92 def __call__(self, language_code: str, reporter: Reporter = None 

93 ) -> LanguageModuleT: 

94 try: 

95 return self.cache[language_code] 

96 except KeyError: 

97 pass 

98 for tag in normalize_language_tag(language_code): 

99 tag = tag.replace('-', '_') # '-' not valid in module names 

100 module = self.import_from_packages(tag, reporter) 

101 if module is not None: 

102 break 

103 else: 

104 if reporter: 

105 reporter.warning(self.warn_msg % language_code) 

106 if self.fallback: 

107 module = self.import_from_packages(self.fallback) 

108 if reporter and (language_code != 'en'): 

109 reporter.info(f'Using {module} for language "{language_code}".') 

110 self.cache[language_code] = module 

111 return module 

112 

113 def __class_getitem__(cls, name): 

114 return cls 

115 

116 

117get_language: LanguageImporter[LanguageModule] = LanguageImporter()