Xqt has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1246751?usp=email )

Change subject: site: raise NotImplementedError in BaseSite.__getattr__
......................................................................

site: raise NotImplementedError in BaseSite.__getattr__

- Change __getattr__ to raise NotImplementedError instead of AttributeError
- when a Family method/attribute exists but cannot be delegated.
- This improves clarity when delegated methods are missing or
- misconfigured in the family file.
- Update docstring to reflect the new behavior and move it to BaseSite
  docstring to be visible in the Sphinx documentation.

Bug: T417961
Change-Id: I3d0585eb53e9ecc0a33edc93ef8dd599a3a3a4c7
---
M pywikibot/site/_basesite.py
1 file changed, 47 insertions(+), 27 deletions(-)

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




diff --git a/pywikibot/site/_basesite.py b/pywikibot/site/_basesite.py
index 55d54bc..19a34a5 100644
--- a/pywikibot/site/_basesite.py
+++ b/pywikibot/site/_basesite.py
@@ -33,7 +33,40 @@

 class BaseSite(ComparableMixin):

-    """Site methods that are independent of the communication interface."""
+    """Site methods that are independent of the communication interface.
+
+    .. hint::
+       :class:`BaseSite` delegates undefined method calls to the
+       corresponing :class:`family.Family` object by its
+       :meth:`__getattr__` method. The working method is described below.
+
+       Only public Family instance methods are delegated. A method is
+       considered delegatable if:
+
+       - it is a bound instance method of Family,
+       - it is public (name does not start with '_'),
+       - its first logical parameter is *code*.
+
+       .. note::
+          For performance reasons, the method signature is inspected
+          via the method's ``__code__`` object instead of
+          ``inspect.signature()``. This avoids expensive generic
+          introspection in this hot path and is safe because Family
+          methods are guaranteed to be pure Python.
+
+       .. versionchanged:: 9.0
+          Only delegate to public Family methods which have ``code`` as
+          first parameter.
+       .. versionchanged:: 11.0
+          Use direct ``__code__`` inspection instead of
+          ``inspect.signature()`` to significantly improve attribute
+          access performance.
+       .. versionchanged:: 11.1
+          :meth:`__getattr__` raises NotImplementedError instead of
+          AttributeError if a Family method or attribute exists but
+          cannot be delegated. This can happen if *name* is not a method
+          or the first parameter is not *code*.
+    """

     def __init__(self, code: str, fam=None, user: str | None = None) -> None:
         """Initializer.
@@ -181,32 +214,14 @@
         return self._username

     def __getattr__(self, name: str):
-        """Delegate undefined methods calls to the Family object.
-
-        Only public :class:`Family instance methods are delegated.
-
-        A method is considered delegatable if:
-        - it is a bound instance method of Family,
-        - it is public (name does not start with '_'),
-        - its first logical parameter is *code*.
-
-        .. note::
-           For performance reasons, the method signature is inspected
-           via the method's ``__code__`` object instead of
-           ``inspect.signature()``. This avoids expensive generic
-           introspection in this hot path and is safe because Family
-           methods are guaranteed to be pure Python.
-
-        .. versionchanged:: 9.0
-           Only delegate to public Family methods which have ``code`` as
-           first parameter.
-        .. versionchanged:: 11.0
-           Use direct ``__code__`` inspection instead of
-           ``inspect.signature()`` to significantly improve attribute
-           access performance.
-        """
+        """Delegate undefined methods calls to the Family object."""
+        # See description in BaseSite class documentation
+        msg = f'{type(self).__name__} instance has no attribute {name!r}'
         if not name.startswith('_'):
             obj = getattr(self.family, name, None)
+            if not obj:
+                raise AttributeError(msg) from None
+
             if inspect.ismethod(obj):
                 code = obj.__code__
                 params = code.co_varnames[:code.co_argcount]
@@ -216,8 +231,13 @@
                         method.__doc__ = obj.__doc__
                     return method

-        raise AttributeError(f'{type(self).__name__} instance has no '
-                             f'attribute {name!r}') from None
+            raise NotImplementedError(
+                f"'{name}()' method of {type(self).__name__} is not "
+                f'implemented. Maybe the {self.family}_family.py family file'
+                ' is malformed'
+            ) from None
+
+        raise AttributeError(msg) from None

     def __str__(self) -> str:
         """Return string representing this Site's name and code."""

--
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1246751?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: I3d0585eb53e9ecc0a33edc93ef8dd599a3a3a4c7
Gerrit-Change-Number: 1246751
Gerrit-PatchSet: 4
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Matěj Suchánek <[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