Skip to content

Commit 3a7922e

Browse files
committed
Turn client module into a dynamic binding registry
1 parent 08fb01c commit 3a7922e

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

juju/client/client.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'''Replace auto-generated classes with our own, where necessary.
22
'''
3+
import sys
34

45
from . import _client, _definitions, overrides # isort:skip
6+
from .old_clients import _client as _2_9_client
7+
from .old_clients import _definitions as _2_9_definitions
58

69
for o in overrides.__all__:
710
if "Facade" not in o:
@@ -10,6 +13,8 @@
1013
# the ref in _client (import shenanigans are fun!)
1114
setattr(_definitions, o, getattr(overrides, o))
1215
setattr(_client, o, getattr(overrides, o))
16+
setattr(_2_9_definitions, o, getattr(overrides, o))
17+
setattr(_2_9_client, o, getattr(overrides, o))
1318
# We shouldn't be overriding Facades!
1419
else:
1520
raise ValueError(
@@ -29,5 +34,59 @@
2934
for a in dir(o_type):
3035
if not a.startswith('_'):
3136
setattr(c_type, a, getattr(o_type, a))
37+
# This unfortunately needs to be a separate loop
38+
for old_client_version in _client.OLD_CLIENTS.values():
39+
try:
40+
c_type = getattr(old_client_version, o)
41+
except AttributeError:
42+
# Not all the _client<version> modules may have the
43+
# facade. That's okay -- we just skip over them.
44+
continue
45+
o_type = getattr(overrides, o)
46+
for a in dir(o_type):
47+
if not a.startswith('_'):
48+
setattr(c_type, a, getattr(o_type, a))
3249

3350
from ._client import * # noqa, isort:skip
51+
52+
class ClientModuleClass:
53+
def __init__(self):
54+
"""
55+
new_client (bool): True if we're working with juju>3.0
56+
"""
57+
self.new_client = None
58+
self.defs = _definitions
59+
self.client_module = _client
60+
61+
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
62+
63+
def __getattr__(self, item):
64+
if self.new_client is None:
65+
raise RuntimeError("ClientModule version is not yet set. Is it 2.9? Or is it >3.0")
66+
if 'Facade' in item:
67+
return getattr(self.client_module, item)
68+
return getattr(self.defs, item)
69+
70+
def set_new_client(self, server_version):
71+
self.new_client = server_version.startswith('3.')
72+
if not self.new_client:
73+
self.defs = _2_9_definitions
74+
self.client_module = _2_9_client
75+
76+
"""
77+
This is basically a hack to turn this module into a dynamic
78+
binding registry, by replacing this module object with a class
79+
instance that acts like a module in sys.modules in the runtime.
80+
81+
Based on the value of the 'new_client' variable
82+
(True if >3.0, False if 2.9) we dynamically change the modules
83+
from which we get the actual bindings behind the scenes.
84+
Theoretically the new_client should only be set once, but we
85+
have to keep it dynamic since there's no way for libjuju to
86+
know when and with which juju version a connection will be
87+
established.
88+
89+
Bindings for >3.0 are coming from _client & _definitions
90+
Bindings for 2.9 are coming from _2_9_client & _2_9_client
91+
"""
92+
sys.modules[__name__] = ClientModuleClass()

juju/client/connection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,9 @@ async def _connect_with_login(self, endpoints):
825825
macaroonJSON = result.get('discharge-required')
826826
if macaroonJSON is None:
827827
self.info = result
828+
# Whenever a connection is established, set
829+
# the new_client flag in the Client module
830+
client.set_new_client(self.info['server-version'])
828831
success = True
829832
return result
830833
macaroon = bakery.Macaroon.from_dict(macaroonJSON)

0 commit comments

Comments
 (0)