@@ -98,8 +98,8 @@ template = Template(
98
98
99
99
# Now we can use the template in combination with the Endpoint:
100
100
101
- from ops import CharmBase
102
- from endpoint_wrapper import Endpoint, ValidationError
101
+ from ops.charm import CharmBase
102
+ from endpoint_wrapper import Endpoint, ValidationError, databag_valid, validate_databag
103
103
104
104
class MyCharm (CharmBase ):
105
105
META = {' requires' : {' foo' : {' interface' : ' bar' }}}
@@ -108,24 +108,24 @@ class MyCharm(CharmBase):
108
108
super ().__init__ (* args)
109
109
self .foo = Endpoint(
110
110
self , ' foo' ,
111
- template = template,
112
- # we can omit `role` and it will be guessed from META, but if we do
113
- # provide it, we get nice type hints below
114
- role = ' requirer' ,
111
+ requirer_template = template,
115
112
on_changed = self ._on_foo_changed
116
113
)
117
114
118
115
# We are the requirer, and our template says that the local app data
119
116
# model for the requirer is RequirerAppModel; so we expect
120
- # local_app_data to be a DataWrapper[ RequirerAppModel]
117
+ # local_app_data to have inferred type = RequirerAppModel
121
118
local_app_data = self .foo.relations[0 ].local_app_data
122
- # so this will work although you have to type it manually
119
+ # getitem notation will still work (for legacy compatibility),
120
+ # however you will have to type it manually (with more modern python
121
+ # versions you might be able to pass a TypedDict instance and get
122
+ # those type annotations too).
123
123
foo_value: int = local_app_data[' foo' ]
124
124
125
125
# using dot notation:
126
126
# the IDE will autocomplete `.foo` for you, and mypy will know that foo_value_dot: int
127
127
foo_value_dot = local_app_data.foo
128
- # mypy will bash you here, because `.foo` is typed as an int, and 2.3 is a float...
128
+ # mypy will smite you here, because `.foo` is typed as an int, and 2.3 is a float...
129
129
local_app_data.foo = 2.3
130
130
131
131
# equivalent to adding an on_joined kwarg to the Endpoint:
@@ -134,36 +134,56 @@ class MyCharm(CharmBase):
134
134
def _on_foo_changed (self , event ):
135
135
# we can check whether:
136
136
137
- # local application data is valid:
138
- if self .foo.__local_app_data_valid :
137
+ # local data (app and unit) is valid:
138
+ if self .foo.local_valid :
139
139
self .do_stuff()
140
140
141
- # remote data is valid: (for all related apps, for all related units).
141
+ # FYI there are methods for checking individual databag validity, but they are private
142
+ # for now:
143
+ # if self.foo._local_units_data_valid: ...
144
+
145
+ # remote data (app and unit) is valid: (for all related apps, for all related units).
142
146
if self .foo.remote_valid:
143
147
self .do_stuff()
144
148
145
- # all data is valid: (all remote and local data ).
149
+ # all data is valid: (all remote and local databags ).
146
150
if self .foo.valid:
147
151
self .do_stuff()
148
152
149
153
# we can also idiomatically read/write data
150
154
# this charm implements the requirer side of foo, so we have to look at RequirerAppModel.
151
155
152
156
for local_app_data in self .foo.local_apps_data.values():
153
- local_app_data[ ' foo' ] = 42
157
+ local_app_data. foo = 42
154
158
# equivalent to:
155
159
# local_app_data.foo = 42 # mypy will understand this!
156
160
157
161
# since we installed pydantic:
158
162
try :
159
- local_app_data[ ' foo' ] = 42.3
163
+ local_app_data. foo = 42.3
160
164
except ValidationError:
161
- pass
165
+ print (' caught this one!' )
166
+
167
+ # also we can
168
+ assert databag_valid(local_app_data) is True
169
+
170
+ # or
171
+ try :
172
+ validate_databag(local_app_data)
173
+ except ValidationError:
174
+ print (' caught this one too!' )
162
175
163
176
def _on_foo_joined (self , event ):
164
- # we can 'wrap' an event's relation idiomatically:
165
- foo_relation = self .foo.wrap(event.relation)
166
- assert foo_relation.remote_app_data.valid
177
+ # if we are within the context of an event that Endpoint wraps,
178
+ # we can grab the Endpoint's `current` relation
179
+ self .foo.current.local_unit_data.foo = 43
180
+
181
+ def _on_config_changed (self ):
182
+ # in non-relation-event handlers, we cannot use `current` but we can
183
+ # 'wrap' an existing ops.model.Relation object idiomatically:
184
+ foo_relation = self .foo.wrap(self .model.relations[' foo' ][0 ])
185
+ foo_relation.local_unit_data.foo = 42
186
+ assert databag_valid(foo_relation.remote_app_data)
167
187
```
168
188
169
189
# Publishing
0 commit comments