1 from collections
import namedtuple
4 from .facade
import ReturnMapping
, Type
, TypeEncoder
6 from .import _definitions
23 """A single websocket delta.
25 :ivar entity: The entity name, e.g. 'unit', 'application'
28 :ivar type: The delta type, e.g. 'add', 'change', 'remove'
31 :ivar data: The raw delta data
34 NOTE: The 'data' variable above is being incorrectly cross-linked by a
35 Sphinx bug: https://github.com/sphinx-doc/sphinx/issues/2549
38 _toSchema
= {'deltas': 'deltas'}
39 _toPy
= {'deltas': 'deltas'}
41 def __init__(self
, deltas
=None):
43 :param deltas: [str, str, object]
48 Change
= namedtuple('Change', 'entity type data')
49 change
= Change(*self
.deltas
)
51 self
.entity
= change
.entity
52 self
.type = change
.type
53 self
.data
= change
.data
56 def from_json(cls
, data
):
57 return cls(deltas
=data
)
60 class ResourcesFacade(Type
):
61 """Patch parts of ResourcesFacade to make it work.
64 @ReturnMapping(_client
.AddPendingResourcesResult
)
65 async def AddPendingResources(self
, application_tag
, charm_url
, resources
):
66 """Fix the calling signature of AddPendingResources.
68 The ResourcesFacade doesn't conform to the standard facade pattern in
69 the Juju source, which leads to the schemagened code not matching up
70 properly with the actual calling convention in the API. There is work
71 planned to fix this in Juju, but we have to work around it for now.
75 resources : typing.Sequence<+T_co>[~CharmResource]<~CharmResource>
76 Returns -> typing.Union[_ForwardRef('ErrorResult'),
77 typing.Sequence<+T_co>[str]]
79 # map input types to rpc msg
81 msg
= dict(type='Resources',
82 request
='AddPendingResources',
85 _params
['tag'] = application_tag
86 _params
['url'] = charm_url
87 _params
['resources'] = resources
88 reply
= await self
.rpc(msg
)
92 class AllWatcherFacade(Type
):
94 Patch rpc method of allwatcher to add in 'id' stuff.
97 async def rpc(self
, msg
):
98 if not hasattr(self
, 'Id'):
99 client
= _client
.ClientFacade
.from_connection(self
.connection
)
101 result
= await client
.WatchAll()
102 self
.Id
= result
.watcher_id
105 result
= await self
.connection
.rpc(msg
, encoder
=TypeEncoder
)
109 class Number(_definitions
.Number
):
111 This type represents a semver string.
113 Because it is not standard JSON, the typical from_json parsing fails and
114 the parsing must be handled specially.
116 See https://github.com/juju/version for more info.
118 numberPat
= re
.compile(r
'^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?$') # noqa
120 def __init__(self
, major
=None, minor
=None, patch
=None, tag
=None,
121 build
=None, **unknown_fields
):
129 self
.major
= int(major
or '0')
130 self
.minor
= int(minor
or '0')
131 self
.patch
= int(patch
or '0')
133 self
.build
= int(build
or '0')
136 return '<Number major={} minor={} patch={} tag={} build={}>'.format(
137 self
.major
, self
.minor
, self
.patch
, self
.tag
, self
.build
)
140 return self
.serialize()
142 def __eq__(self
, other
):
144 isinstance(other
, type(self
)) and
145 other
.major
== self
.major
and
146 other
.minor
== self
.minor
and
147 other
.tag
== self
.tag
and
148 other
.patch
== self
.patch
and
149 other
.build
== self
.build
)
152 def from_json(cls
, data
):
154 if isinstance(data
, cls
):
158 elif isinstance(data
, dict):
160 elif isinstance(data
, str):
161 match
= cls
.numberPat
.match(data
)
164 'major': match
.group(1),
165 'minor': match
.group(2),
166 'tag': match
.group(3),
167 'patch': match
.group(4),
168 'build': (match
.group(5)[1:] if match
.group(5)
172 raise TypeError('Unable to parse Number version string: '
175 for k
, v
in parsed
.items():
176 d
[cls
._toPy
.get(k
, k
)] = v
183 s
= "{}.{}.{}".format(self
.major
, self
.minor
, self
.patch
)
185 s
= "{}.{}-{}{}".format(self
.major
, self
.minor
, self
.tag
,
188 s
= "{}.{}".format(s
, self
.build
)
192 return self
.serialize()
195 class Binary(_definitions
.Binary
):
197 This type represents a semver string with additional series and arch info.
199 Because it is not standard JSON, the typical from_json parsing fails and
200 the parsing must be handled specially.
202 See https://github.com/juju/version for more info.
204 binaryPat
= re
.compile(r
'^(\d{1,9})\.(\d{1,9})(?:\.|-([a-z]+))(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$') # noqa
206 def __init__(self
, number
=None, series
=None, arch
=None, **unknown_fields
):
212 self
.number
= Number
.from_json(number
)
217 return '<Binary number={} series={} arch={}>'.format(
218 self
.number
, self
.series
, self
.arch
)
221 return self
.serialize()
223 def __eq__(self
, other
):
225 isinstance(other
, type(self
)) and
226 other
.number
== self
.number
and
227 other
.series
== self
.series
and
228 other
.arch
== self
.arch
)
231 def from_json(cls
, data
):
233 if isinstance(data
, cls
):
237 elif isinstance(data
, dict):
239 elif isinstance(data
, str):
240 match
= cls
.binaryPat
.match(data
)
244 'major': match
.group(1),
245 'minor': match
.group(2),
246 'tag': match
.group(3),
247 'patch': match
.group(4),
248 'build': (match
.group(5)[1:] if match
.group(5)
251 'series': match
.group(6),
252 'arch': match
.group(7),
255 raise TypeError('Unable to parse Binary version string: '
258 for k
, v
in parsed
.items():
259 d
[cls
._toPy
.get(k
, k
)] = v
264 return "{}-{}-{}".format(self
.number
.serialize(),
265 self
.series
, self
.arch
)
268 return self
.serialize()
271 class ConfigValue(_definitions
.ConfigValue
):
273 return '<{} source={} value={}>'.format(type(self
).__name
__,