SF.net SVN: docutils:[9852 ] trunk/docutils/docutils

aa-turner--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 9852
          http://sourceforge.net/p/docutils/code/9852
Author:   aa-turner
Date:     2024-08-03 21:31:01 +0000 (Sat, 03 Aug 2024)
Log Message:
-----------
Add type hints to ``docutils.languages``

Modified Paths:
--------------
    trunk/docutils/docutils/languages/__init__.py
    trunk/docutils/docutils/parsers/rst/languages/__init__.py

Modified: trunk/docutils/docutils/languages/__init__.py
===================================================================
--- trunk/docutils/docutils/languages/__init__.py	2024-08-03 12:35:42 UTC (rev 9851)
+++ trunk/docutils/docutils/languages/__init__.py	2024-08-03 21:31:01 UTC (rev 9852)
@@ -9,13 +9,31 @@
 This package contains modules for language-dependent features of Docutils.
 """
 
+from __future__ import annotations
+
 __docformat__ = 'reStructuredText'
 
 from importlib import import_module
+from typing import TYPE_CHECKING, overload
 
 from docutils.utils import normalize_language_tag
 
+if TYPE_CHECKING:
+    import types
+    from typing import NoReturn, Protocol, TypeVar
 
+    from docutils.utils import Reporter
+
+    class LanguageModule(Protocol):
+        __name__: str
+
+        labels: dict[str, str]
+        bibliographic_fields: dict[str, str]
+        author_separators: list[str]
+
+    LanguageModuleT = TypeVar('LanguageModuleT')
+
+
 class LanguageImporter:
     """Import language modules.
 
@@ -32,14 +50,16 @@
     # TODO: use a dummy module returning empty strings?, configurable?
 
     def __init__(self) -> None:
-        self.cache = {}
+        self.cache: dict[str, LanguageModuleT] = {}
 
-    def import_from_packages(self, name, reporter=None):
+    def import_from_packages(
+        self, name: str, reporter: Reporter = None
+    ) -> LanguageModuleT:
         """Try loading module `name` from `self.packages`."""
         module = None
         for package in self.packages:
             try:
-                module = import_module(package+name)
+                module = import_module(package + name)
                 self.check_content(module)
             except (ImportError, AttributeError):
                 if reporter and module:
@@ -51,14 +71,26 @@
             break
         return module
 
-    def check_content(self, module):
+    @overload
+    def check_content(self, module: LanguageModule) -> None:
+        ...
+
+    @overload
+    def check_content(self, module: types.ModuleType) -> NoReturn:
+        ...
+
+    def check_content(self, module: LanguageModule | types.ModuleType) -> None:
         """Check if we got a Docutils language module."""
-        if not (isinstance(module.labels, dict)
-                and isinstance(module.bibliographic_fields, dict)
-                and isinstance(module.author_separators, list)):
+        if not (
+            isinstance(module.labels, dict)
+            and isinstance(module.bibliographic_fields, dict)
+            and isinstance(module.author_separators, list)
+        ):
             raise ImportError
 
-    def __call__(self, language_code, reporter=None):
+    def __call__(
+        self, language_code: str, reporter: Reporter = None
+    ) -> LanguageModuleT:
         try:
             return self.cache[language_code]
         except KeyError:
@@ -74,10 +106,12 @@
             if self.fallback:
                 module = self.import_from_packages(self.fallback)
         if reporter and (language_code != 'en'):
-            reporter.info('Using %s for language "%s".'
-                          % (module, language_code))
+            reporter.info(f'Using {module} for language "{language_code}".')
         self.cache[language_code] = module
         return module
 
+    def __class_getitem__(cls, name):
+        return cls
 
-get_language = LanguageImporter()
+
+get_language: LanguageImporter[LanguageModule] = LanguageImporter()

Modified: trunk/docutils/docutils/parsers/rst/languages/__init__.py
===================================================================
--- trunk/docutils/docutils/parsers/rst/languages/__init__.py	2024-08-03 12:35:42 UTC (rev 9851)
+++ trunk/docutils/docutils/parsers/rst/languages/__init__.py	2024-08-03 21:31:01 UTC (rev 9852)
@@ -10,12 +10,25 @@
 reStructuredText.
 """
 
+from __future__ import annotations
+
 __docformat__ = 'reStructuredText'
 
+from typing import TYPE_CHECKING, overload
 
 from docutils.languages import LanguageImporter
 
+if TYPE_CHECKING:
+    import types
+    from typing import NoReturn, Protocol
 
+    class RSTLanguageModule(Protocol):
+        __name__: str
+
+        directives: dict[str, str]
+        roles: dict[str, str]
+
+
 class RstLanguageImporter(LanguageImporter):
     """Import language modules.
 
@@ -30,11 +43,23 @@
     warn_msg = 'rST localisation for language "%s" not found.'
     fallback = None
 
-    def check_content(self, module):
+    @overload
+    def check_content(self, module: RSTLanguageModule) -> None:
+        ...
+
+    @overload
+    def check_content(self, module: types.ModuleType) -> NoReturn:
+        ...
+
+    def check_content(
+        self, module: RSTLanguageModule | types.ModuleType
+    ) -> None:
         """Check if we got an rST language module."""
-        if not (isinstance(module.directives, dict)
-                and isinstance(module.roles, dict)):
+        if not (
+            isinstance(module.directives, dict)
+            and isinstance(module.roles, dict)
+        ):
             raise ImportError
 
 
-get_language = RstLanguageImporter()
+get_language: LanguageImporter[RSTLanguageModule] = RstLanguageImporter()

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.