I've been beating my head against the wall trying to figure out how to build a Mock of APISite. If I do the obvious:
site = mocker.MagicMock(spec=APISite)
I end up with:
AttributeError: Mock object has no attribute 'encodings'
Yet, if I hand-build a Site object, sure enough it does:
import pywikibot site = pywikibot.Site("en", "wikipedia") site.encodings()
('utf-8', 'iso-8859-1')
To make a long story short, eventually I found this bit of magic in _basesite.py:
def __getattr__(self, attr): """Delegate undefined methods calls to the Family object.""" if hasattr(self.__class__, attr): return getattr(self.__class__, attr) try: method = getattr(self.family, attr) if not callable(method): raise AttributeError f = functools.partial(method, self.code) if hasattr(method, '__doc__'): f.__doc__ = method.__doc__ return f except AttributeError: raise AttributeError("{} instance has no attribute '{}'" .format(self.__class__.__name__, attr))
WTF? I mean, I see what it's doing, but why go to this level of obfuscation? It's basically reimplementing class inheritance manually (and in a way which is totally beyond the ability of Mock to understand).
Hi,
It is not a reimplementation of class inheritance. The API method is delegated to the Family method for its site code only e.g. site.encodings() calls site.family.encodings(site.code).
Best xqt
Am 12.12.2022 um 02:50 schrieb Roy Smith roy@panix.com:
I've been beating my head against the wall trying to figure out how to build a Mock of APISite. If I do the obvious:
site = mocker.MagicMock(spec=APISite)
I end up with:
AttributeError: Mock object has no attribute 'encodings'
Yet, if I hand-build a Site object, sure enough it does:
import pywikibot site = pywikibot.Site("en", "wikipedia") site.encodings()
('utf-8', 'iso-8859-1')
To make a long story short, eventually I found this bit of magic in _basesite.py:
def __getattr__(self, attr): """Delegate undefined methods calls to the Family object.""" if hasattr(self.__class__, attr): return getattr(self.__class__, attr) try: method = getattr(self.family, attr) if not callable(method): raise AttributeError f = functools.partial(method, self.code) if hasattr(method, '__doc__'): f.__doc__ = method.__doc__ return f except AttributeError: raise AttributeError("{} instance has no attribute '{}'" .format(self.__class__.__name__, attr))
WTF? I mean, I see what it's doing, but why go to this level of obfuscation? It's basically reimplementing class inheritance manually (and in a way which is totally beyond the ability of Mock to understand). _______________________________________________ pywikibot mailing list -- pywikibot@lists.wikimedia.org To unsubscribe send an email to pywikibot-leave@lists.wikimedia.org
The more I look at this code, the more confused I get. I'm looking BaseSite.__getattr__() on master https://github.com/wikimedia/pywikibot/blob/d6262ba6536cd37d2ea655abd0257bf8fc5bcbb6/pywikibot/site/_basesite.py#L187. It starts out:
def __getattr__(self, attr): """Delegate undefined methods calls to the Family object.""" if hasattr(self.__class__, attr): return getattr(self.__class__, attr)
how could that branch ever be taken? __getattr__() is called when "when the default attribute access fails with an AttributeError". Which means the hasattr() call will always return False.
On Dec 11, 2022, at 10:20 PM, info@gno.de wrote:
Hi,
It is not a reimplementation of class inheritance. The API method is delegated to the Family method for its site code only e.g. site.encodings() calls site.family.encodings(site.code).
Best xqt
Am 12.12.2022 um 02:50 schrieb Roy Smith roy@panix.com:
I've been beating my head against the wall trying to figure out how to build a Mock of APISite. If I do the obvious:
site = mocker.MagicMock(spec=APISite)
I end up with:
AttributeError: Mock object has no attribute 'encodings'
Yet, if I hand-build a Site object, sure enough it does:
import pywikibot site = pywikibot.Site("en", "wikipedia") site.encodings()
('utf-8', 'iso-8859-1')
To make a long story short, eventually I found this bit of magic in _basesite.py:
def __getattr__(self, attr): """Delegate undefined methods calls to the Family object.""" if hasattr(self.__class__, attr): return getattr(self.__class__, attr) try: method = getattr(self.family, attr) if not callable(method): raise AttributeError f = functools.partial(method, self.code) if hasattr(method, '__doc__'): f.__doc__ = method.__doc__ return f except AttributeError: raise AttributeError("{} instance has no attribute '{}'" .format(self.__class__.__name__, attr))
WTF? I mean, I see what it's doing, but why go to this level of obfuscation? It's basically reimplementing class inheritance manually (and in a way which is totally beyond the ability of Mock to understand). _______________________________________________ pywikibot mailing list -- pywikibot@lists.wikimedia.org To unsubscribe send an email to pywikibot-leave@lists.wikimedia.org
pywikibot mailing list -- pywikibot@lists.wikimedia.org To unsubscribe send an email to pywikibot-leave@lists.wikimedia.org