VcamX added a subscriber: XZise.
VcamX added a comment.

In https://phabricator.wikimedia.org/T106512#1487590, @VcamX wrote:

> What happened on Python 2.7.0 is so weird.
>  
> https://github.com/wikimedia/pywikibot-core/blob/master/tests/namespace_tests.py#L222
>
>   self.assertEqual(Namespace.resolve([6]), [file_ns])
>
> The first argument is what Namespace.resolve([6]) returns, that's a list only 
> containing a Namespace object. The second argument is a list only containing 
> `file_ns`, that's also a Namespace object.
>  These two object should be equal:
>  `__eq__` of `Namespace`: 
> https://github.com/wikimedia/pywikibot-core/blob/master/pywikibot/site.py#L332
>  `[file_ns]`: 
> https://ci.appveyor.com/project/VcamX/pywikibot-core/build/1.0.96#L4553
>  `Namespace.resolve([6])`: 
> https://ci.appveyor.com/project/VcamX/pywikibot-core/build/1.0.94#L4516
>
> If we just test `self.assertEqual(Namespace.resolve([6])[0], file_ns)`, that 
> will be fine.
>  If we wrap them inside a list, they're not equal.


@jayvdb, @XZise, I think it's about `TestCase` of Python 2.7.0 and 
`CapturingTestCase` of Pywikibot:

Related methods of `TestCase` in unittest lib of Python 2.7.0:

`__init__`: 
https://github.com/python/cpython/blob/6d9a901fd68d467a5d91889b7a11e3a29a134068/Lib/unittest/case.py#L173

  def __init__(self, methodName='runTest'):
      ......
      
      self._type_equality_funcs = {}
      self.addTypeEqualityFunc(dict, self.assertDictEqual)
      self.addTypeEqualityFunc(list, self.assertListEqual)
      self.addTypeEqualityFunc(tuple, self.assertTupleEqual)
      self.addTypeEqualityFunc(set, self.assertSetEqual)
      self.addTypeEqualityFunc(frozenset, self.assertSetEqual)
      self.addTypeEqualityFunc(unicode, self.assertMultiLineEqual)

`_getAssertEqualityFunc`: 
https://github.com/python/cpython/blob/6d9a901fd68d467a5d91889b7a11e3a29a134068/Lib/unittest/case.py#L458

  def _getAssertEqualityFunc(self, first, second):
      if type(first) is type(second):
          asserter = self._type_equality_funcs.get(type(first))
          if asserter is not None:
              return asserter
      return self._baseAssertEqual

`assertEqual`: 
https://github.com/python/cpython/blob/6d9a901fd68d467a5d91889b7a11e3a29a134068/Lib/unittest/case.py#L489

  def assertEqual(self, first, second, msg=None):
      assertion_func = self._getAssertEqualityFunc(first, second)
      assertion_func(first, second, msg=msg)

**NOTE**: some `self.assertXXXEqual` methods are referenced without called in 
`__init__`.

Related methods of`CapturingTestCase` in `tests.aspects` of Pywikibot:

`patch_assert`: 
https://github.com/wikimedia/pywikibot-core/blob/master/tests/aspects.py#L986

  def patch_assert(self, assertion):
      """Execute process_assert when the assertion is called."""
      def inner_assert(*args, **kwargs):
          assert self._patched is False
          self._patched = True
          try:
              self.process_assert(assertion, *args, **kwargs)
          finally:
              self._patched = False
      return inner_assert

`__getattribute__` of `CapturingTestCase`: 
https://github.com/wikimedia/pywikibot-core/blob/master/tests/aspects.py#L997:

  def __getattribute__(self, attr):
      """Patch assertions if enabled."""
      result = super(CapturingTestCase, self).__getattribute__(attr)
      if attr.startswith('assert') and not self._patched:
          return self.patch_assert(result)
      else:
          return result

`__getattribute__` will check the name of attribute and filter the prefix of 
`assert` if it's not patched, then patch it. That's the problem.

Remember that in `__init__` of `TestCase`, some `self.assertXXXEqual` methods 
are referenced without called. In `__init__`, `self._type_equality_funcs` is a 
dict mapping from type to type-related method which is used to get the 
corresponding assert method for type. Because of the custom `__getattribute__`, 
that causes `self._type_equality_funcs` store the patched methods instead of 
the right ones.

When we call `assertEqual`, we get the patched one, which set `self._patch` to 
True. Then the original `assertEqual` will get the patched `assertListEqual`, 
which will check `self._patch` first before executing the main body. So the 
assertion will fail.


TASK DETAIL
  https://phabricator.wikimedia.org/T106512

EMAIL PREFERENCES
  https://phabricator.wikimedia.org/settings/panel/emailpreferences/

To: VcamX
Cc: XZise, gerritbot, VcamX, Aklapper, jayvdb, pywikibot-bugs-list, Malyacko, 
P.Copp



_______________________________________________
pywikibot-bugs mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/pywikibot-bugs

Reply via email to