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

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

66 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 

17from typing import TYPE_CHECKING, overload 

18 

19from docutils.utils import normalize_language_tag 

20 

21if TYPE_CHECKING: 

22 import types 

23 from typing import NoReturn, Protocol, TypeVar 

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') 

35 

36 

37class LanguageImporter: 

38 """Import language modules. 

39 

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

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

42 

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

44 and fall back to English. 

45 """ 

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

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

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

49 fallback = 'en' 

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

51 

52 def __init__(self) -> None: 

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

54 

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

56 ) -> LanguageModuleT: 

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

58 module = None 

59 for package in self.packages: 

60 try: 

61 module = import_module(package + name) 

62 self.check_content(module) 

63 except (ImportError, AttributeError): 

64 if reporter and module: 

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

66 'Docutils language module.') 

67 elif reporter: 

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

69 continue 

70 break 

71 return module 

72 

73 @overload 

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

75 ... 

76 

77 @overload 

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

79 ... 

80 

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

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

83 if not ( 

84 isinstance(module.labels, dict) 

85 and isinstance(module.bibliographic_fields, dict) 

86 and isinstance(module.author_separators, list) 

87 ): 

88 raise ImportError 

89 

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

91 ) -> LanguageModuleT: 

92 try: 

93 return self.cache[language_code] 

94 except KeyError: 

95 pass 

96 for tag in normalize_language_tag(language_code): 

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

98 module = self.import_from_packages(tag, reporter) 

99 if module is not None: 

100 break 

101 else: 

102 if reporter: 

103 reporter.warning(self.warn_msg % language_code) 

104 if self.fallback: 

105 module = self.import_from_packages(self.fallback) 

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

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

108 self.cache[language_code] = module 

109 return module 

110 

111 def __class_getitem__(cls, name): 

112 return cls 

113 

114 

115get_language: LanguageImporter[LanguageModule] = LanguageImporter()