jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1187937?usp=email )

Change subject: Refactor Siteinfo class
......................................................................

Refactor Siteinfo class

- Add type hints
- Update docstrings and remove pre-1.12 hints
- Simplify _post_process

Change-Id: If7cda4be511fc86e6102fb8c59e0b65417fb48e5
---
M pywikibot/site/_siteinfo.py
1 file changed, 58 insertions(+), 30 deletions(-)

Approvals:
  Xqt: Looks good to me, approved
  jenkins-bot: Verified




diff --git a/pywikibot/site/_siteinfo.py b/pywikibot/site/_siteinfo.py
index 3f79f3b..389947a 100644
--- a/pywikibot/site/_siteinfo.py
+++ b/pywikibot/site/_siteinfo.py
@@ -1,6 +1,6 @@
 """Objects representing site info data contents."""
 #
-# (C) Pywikibot team, 2008-2024
+# (C) Pywikibot team, 2008-2025
 #
 # Distributed under the terms of the MIT license.
 #
@@ -11,13 +11,17 @@
 import re
 from collections.abc import Container
 from contextlib import suppress
-from typing import Any
+from typing import TYPE_CHECKING, Any, Literal

 import pywikibot
 from pywikibot.exceptions import APIError
 from pywikibot.tools.collections import EMPTY_DEFAULT


+if TYPE_CHECKING:
+    from pywikibot.site import APISite
+
+
 class Siteinfo(Container):

     """A 'dictionary' like container for siteinfo.
@@ -57,48 +61,58 @@
         ],
     }

-    def __init__(self, site) -> None:
-        """Initialise it with an empty cache."""
+    def __init__(self, site: APISite) -> None:
+        """Initialize Siteinfo for a given site with an empty cache."""
         self._site = site
-        self._cache: dict[str, Any] = {}
+        self._cache: dict[str,
+                          tuple[Any, datetime.datetime | Literal[False]]] = {}

     def clear(self) -> None:
-        """Remove all items from Siteinfo.
+        """Clear all cached siteinfo properties.

         .. versionadded:: 7.1
         """
         self._cache.clear()

     @staticmethod
-    def _post_process(prop, data) -> None:
-        """Do some default handling of data.
+    def _post_process(prop: str,
+                      data: dict[str, Any] | list[dict[str, Any]]) -> None:
+        """Convert empty-string boolean properties to actual booleans.

-        Directly modifies data.
+        Modifies *data* in place.
+
+        :param prop: The siteinfo property name (e.g., 'general',
+            'namespaces', 'magicwords')
+        :param data: The raw data returned from the server
         """
         # Be careful with version tests inside this here as it might need to
         # query this method to actually get the version number

         # Convert boolean props from empty strings to actual boolean values
-        if prop in Siteinfo.BOOLEAN_PROPS:
-            # siprop=namespaces and
-            # magicwords has properties per item in result
-            if prop in ('namespaces', 'magicwords'):
-                for index, value in enumerate(data):
-                    # namespaces uses a dict, while magicwords uses a list
-                    key = index if isinstance(data, list) else value
-                    for p in Siteinfo.BOOLEAN_PROPS[prop]:
-                        data[key][p] = p in data[key]
+        if prop not in Siteinfo.BOOLEAN_PROPS:
+            return
+
+        bool_props = Siteinfo.BOOLEAN_PROPS[prop]
+        if prop == 'general':
+            # Direct properties of 'general'
+            for p in bool_props:
+                data[p] = p in data
+        else:
+            # 'namespaces' (dict) or 'magicwords' (list of dicts)
+            items: list[dict[str, Any]]
+            if isinstance(data, dict):
+                items = list(data.values())
+            elif isinstance(data, list):
+                items = data
             else:
-                for p in Siteinfo.BOOLEAN_PROPS[prop]:
-                    data[p] = p in data
+                return  # unexpected format
+
+            for item in items:
+                for p in bool_props:
+                    item[p] = p in item

     def _get_siteinfo(self, prop, expiry) -> dict:
-        """Retrieve a siteinfo property.
-
-        All properties which the site doesn't
-        support contain the default value. Because pre-1.12 no data was
-        returned when a property doesn't exists, it queries each property
-        independently if a property is invalid.
+        """Retrieve one or more siteinfo properties from the server.

         .. seealso:: :api:Siteinfo

@@ -110,6 +124,8 @@
             the dictionary is a tuple of the value and a boolean to save if it
             is the default value.
         """
+        invalid_properties: list[str] = []
+
         def warn_handler(mod, message) -> bool:
             """Return True if the warning is handled."""
             matched = Siteinfo.WARNING_REGEX.fullmatch(message)
@@ -119,11 +135,11 @@
                 return True
             return False

-        props = [prop] if isinstance(prop, str) else prop
+        # Convert to list for consistent iteration
+        props = [prop] if isinstance(prop, str) else list(prop)
         if not props:
             raise ValueError('At least one property name must be provided.')

-        invalid_properties: list[str] = []
         request = self._site._request(
             expiry=pywikibot.config.API_config_expiry
             if expiry is False else expiry,
@@ -134,6 +150,7 @@

         # warnings are handled later
         request._warning_handler = warn_handler
+
         try:
             data = request.submit()
         except APIError as e:
@@ -158,6 +175,7 @@
             pywikibot.log("Unable to get siprop(s) '{}'"
                           .format("', '".join(invalid_properties)))

+        # Process valid properties
         if 'query' in data:
             # If the request is a CachedRequest, use the _cachetime attr.
             cache_time = getattr(
@@ -169,8 +187,16 @@
         return result

     @staticmethod
-    def _is_expired(cache_date, expire):
-        """Return true if the cache date is expired."""
+    def _is_expired(cache_date: datetime.datetime | Literal[False] | None,
+                    expire: datetime.timedelta | Literal[False]) -> bool:
+        """Return true if the cache date is expired.
+
+        :param cache_date: The timestamp when the value was cached, or
+            False if default, None if never.
+        :param expire: Expiry period as timedelta, or False to never
+            expire.
+        :return: True if expired, False otherwise.
+        """
         if isinstance(expire, bool):
             return expire

@@ -215,8 +241,10 @@
                 self._cache[prop] = default_info[prop]
             if key in default_info:
                 return default_info[key]
+
         if key in self._cache['general'][0]:
             return self._cache['general'][0][key], self._cache['general']
+
         return None

     def __getitem__(self, key: str):

--
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1187937?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: If7cda4be511fc86e6102fb8c59e0b65417fb48e5
Gerrit-Change-Number: 1187937
Gerrit-PatchSet: 2
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to