Fix neutron client dict grabbing
Due to a bug in python3.13 [1] the following code will leads to an
emptied dict by the GC even though we hold a reference to the dict.
import gc
class A:
def __init__(self, client):
self.__dict__ = client.__dict__
self.client = client
class B:
def __init__(self):
self.test_attr = "foo"
a = A(B())
print(a.__dict__)
print(a.client.__dict__)
gc.collect()
print("## After gc.collect()")
print(a.__dict__)
print(a.client.__dict__)
# Output with Python 13
{'test_attr': 'foo', 'client': <__main__.B object at 0x73ea355a8590>}
{'test_attr': 'foo', 'client': <__main__.B object at 0x73ea355a8590>}
## After gc.collect()
{'test_attr': 'foo', 'client': <__main__.B object at 0x73ea355a8590>}
{}
# Output with Python 12
{'test_attr': 'foo', 'client': <__main__.B object at 0x79c86f355400>}
{'test_attr': 'foo', 'client': <__main__.B object at 0x79c86f355400>}
## After gc.collect()
{'test_attr': 'foo', 'client': <__main__.B object at 0x79c86f355400>}
{'test_attr': 'foo', 'client': <__main__.B object at 0x79c86f355400>
Our neutron client has this kind of code and therefore failing in
python3.13. This patch adds __getattr__ instead of trying to hold a
direct reference to the __dict__. This seems to work around the
problem.
Co-Authored-By: Johannes Kulik <johannes.kulik@sap.com>
[1] https://github.com/python/cpython/issues/130327
Closes-Bug: #2103413
Change-Id: I87c9fbb9331135674232c6e77d700966a938b0ac
(cherry picked from commit 7d946c4535)
This commit is contained in:
@@ -171,7 +171,7 @@ def refresh_cache(f):
|
||||
|
||||
|
||||
@profiler.trace_cls("neutron_api")
|
||||
class ClientWrapper(clientv20.Client):
|
||||
class ClientWrapper:
|
||||
"""A Neutron client wrapper class.
|
||||
|
||||
Wraps the callable methods, catches Unauthorized,Forbidden from Neutron and
|
||||
@@ -179,16 +179,18 @@ class ClientWrapper(clientv20.Client):
|
||||
"""
|
||||
|
||||
def __init__(self, base_client, admin):
|
||||
# Expose all attributes from the base_client instance
|
||||
self.__dict__ = base_client.__dict__
|
||||
self.base_client = base_client
|
||||
self.admin = admin
|
||||
|
||||
def __getattribute__(self, name):
|
||||
obj = object.__getattribute__(self, name)
|
||||
if callable(obj):
|
||||
obj = object.__getattribute__(self, 'proxy')(obj)
|
||||
return obj
|
||||
def __getattr__(self, name):
|
||||
base_attr = getattr(self.base_client, name)
|
||||
# Each callable base client attr is wrapped so that we can translate
|
||||
# the Unauthorized exception based on if we have an admin client or
|
||||
# not.
|
||||
if callable(base_attr):
|
||||
return self.proxy(base_attr)
|
||||
|
||||
return base_attr
|
||||
|
||||
def proxy(self, obj):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user