It's changing out the base class for ConfigOpts and GroupAttr. When I looked at the collections.abc implementations in Python2 and Python3.7, I noticed there were some differences in how the base classes that comprise collections.abc.Mapping "calculate" attributes.
In the case of Python2, there is a _hasattr() module-level function that is used to determine if a particular attribute *or* method exists. For example, one of the base metaclasses that collections.Mapping derives from is Container, which uses this _hasattr() function to determine if the subclass overrides the __contains__ magic method:
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if _hasattr(C, "__contains__"): return True
return NotImplemented
If we look at Python2's _hasattr() compared to Python3.7's _check_methods() function, we see some differences:
Python2:
def _hasattr(C, attr):
try:
return any(attr in B.__dict__ for B in C.__mro__)
except AttributeError:
# Old-style class
return hasattr(C, attr)
Python 3.7:
def _check_methods(C, *methods):
mro = C.__mro__
for method in methods:
for B in mro:
if method in B.__dict__:
if B.__dict__[method] is None: return NotImplemented break
else:
return NotImplemented
return True
Matt, could this patch have anything to do with this you think?
https:/ /github. com/openstack/ oslo.config/ commit/ 6c951ed373f4437 27a8ea2d10d346a d769285aa2
It's changing out the base class for ConfigOpts and GroupAttr. When I looked at the collections.abc implementations in Python2 and Python3.7, I noticed there were some differences in how the base classes that comprise collections. abc.Mapping "calculate" attributes.
In the case of Python2, there is a _hasattr() module-level function that is used to determine if a particular attribute *or* method exists. For example, one of the base metaclasses that collections.Mapping derives from is Container, which uses this _hasattr() function to determine if the subclass overrides the __contains__ magic method:
@classmethod _(cls, C):
return True
def __subclasshook_
if cls is Container:
if _hasattr(C, "__contains__"):
return NotImplemented
https:/ /github. com/python/ cpython/ blob/2. 7/Lib/_ abcoll. py#L116
Compare this to the Python3.7 Container class's similar implementation:
@classmethod _(cls, C):
def __subclasshook_
if cls is Container:
return _check_methods(C, "__contains__")
return NotImplemented
https:/ /github. com/python/ cpython/ blob/3. 7/Lib/_ collections_ abc.py# L385
If we look at Python2's _hasattr() compared to Python3.7's _check_methods() function, we see some differences:
Python2:
def _hasattr(C, attr):
try:
return any(attr in B.__dict__ for B in C.__mro__)
except AttributeError:
# Old-style class
return hasattr(C, attr)
Python 3.7:
def _check_methods(C, *methods):
return NotImplemented
break
mro = C.__mro__
for method in methods:
for B in mro:
if method in B.__dict__:
if B.__dict__[method] is None:
else:
return NotImplemented
return True
Given the stacktrace that seems to be occurring, I think it might be worthwhile to revert https:/ /github. com/openstack/ oslo.config/ commit/ 6c951ed373f4437 27a8ea2d10d346a d769285aa2 and see if that might indeed be the culprit here.
Just a thought,
-jay