@@ -295,10 +295,21 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
295
295
import json
296
296
import logging
297
297
from abc import ABC , abstractmethod
298
- from collections import namedtuple
298
+ from collections import UserDict , namedtuple
299
299
from datetime import datetime
300
300
from enum import Enum
301
- from typing import Callable , Dict , List , Optional , Set , Tuple , Union
301
+ from typing import (
302
+ Callable ,
303
+ Dict ,
304
+ ItemsView ,
305
+ KeysView ,
306
+ List ,
307
+ Optional ,
308
+ Set ,
309
+ Tuple ,
310
+ Union ,
311
+ ValuesView ,
312
+ )
302
313
303
314
from ops import JujuVersion , Model , Secret , SecretInfo , SecretNotFoundError
304
315
from ops .charm import (
@@ -320,7 +331,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
320
331
321
332
# Increment this PATCH version before using `charmcraft publish-lib` or reset
322
333
# to 0 if you are raising the major API version
323
- LIBPATCH = 29
334
+ LIBPATCH = 31
324
335
325
336
PYDEPS = ["ops>=2.0.0" ]
326
337
@@ -612,6 +623,102 @@ def remove(self, label: str) -> None:
612
623
# Base Data
613
624
614
625
626
+ class DataDict (UserDict [str , str ]):
627
+ """Python Standard Library 'dict' - like representation of Relation Data."""
628
+
629
+ def __init__ (self , relation_data : "Data" , relation_id : int ):
630
+ self .relation_data = relation_data
631
+ self .relation_id = relation_id
632
+
633
+ @property
634
+ def data (self ) -> Dict [str , str ]:
635
+ """Return the full content of the Abstract Relation Data dictionary."""
636
+ result = self .relation_data .fetch_my_relation_data ([self .relation_id ])
637
+ try :
638
+ result_remote = self .relation_data .fetch_relation_data ([self .relation_id ])
639
+ except NotImplementedError :
640
+ result_remote = {self .relation_id : {}}
641
+ if result :
642
+ result_remote [self .relation_id ].update (result [self .relation_id ])
643
+ return result_remote .get (self .relation_id , {})
644
+
645
+ def __setitem__ (self , key : str , item : str ) -> None :
646
+ """Set an item of the Abstract Relation Data dictionary."""
647
+ self .relation_data .update_relation_data (self .relation_id , {key : item })
648
+
649
+ def __getitem__ (self , key : str ) -> str :
650
+ """Get an item of the Abstract Relation Data dictionary."""
651
+ result = None
652
+ if not (result := self .relation_data .fetch_my_relation_field (self .relation_id , key )):
653
+ try :
654
+ result = self .relation_data .fetch_relation_field (self .relation_id , key )
655
+ except NotImplementedError :
656
+ pass
657
+ if not result :
658
+ raise KeyError
659
+ return result
660
+
661
+ def __eq__ (self , d : dict ) -> bool :
662
+ """Equality."""
663
+ return self .data == d
664
+
665
+ def __repr__ (self ) -> str :
666
+ """String representation Abstract Relation Data dictionary."""
667
+ return repr (self .data )
668
+
669
+ def __len__ (self ) -> int :
670
+ """Length of the Abstract Relation Data dictionary."""
671
+ return len (self .data )
672
+
673
+ def __delitem__ (self , key : str ) -> None :
674
+ """Delete an item of the Abstract Relation Data dictionary."""
675
+ self .relation_data .delete_relation_data (self .relation_id , [key ])
676
+
677
+ def has_key (self , key : str ) -> bool :
678
+ """Does the key exist in the Abstract Relation Data dictionary?"""
679
+ return key in self .data
680
+
681
+ def update (self , items : Dict [str , str ]):
682
+ """Update the Abstract Relation Data dictionary."""
683
+ self .relation_data .update_relation_data (self .relation_id , items )
684
+
685
+ def keys (self ) -> KeysView [str ]:
686
+ """Keys of the Abstract Relation Data dictionary."""
687
+ return self .data .keys ()
688
+
689
+ def values (self ) -> ValuesView [str ]:
690
+ """Values of the Abstract Relation Data dictionary."""
691
+ return self .data .values ()
692
+
693
+ def items (self ) -> ItemsView [str , str ]:
694
+ """Items of the Abstract Relation Data dictionary."""
695
+ return self .data .items ()
696
+
697
+ def pop (self , item : str ) -> str :
698
+ """Pop an item of the Abstract Relation Data dictionary."""
699
+ result = self .relation_data .fetch_my_relation_field (self .relation_id , item )
700
+ if not result :
701
+ raise KeyError (f"Item { item } doesn't exist." )
702
+ self .relation_data .delete_relation_data (self .relation_id , [item ])
703
+ return result
704
+
705
+ def __contains__ (self , item : str ) -> bool :
706
+ """Does the Abstract Relation Data dictionary contain item?"""
707
+ return item in self .data .values ()
708
+
709
+ def __iter__ (self ):
710
+ """Iterate through the Abstract Relation Data dictionary."""
711
+ return iter (self .data )
712
+
713
+ def get (self , key : str , default : Optional [str ] = None ) -> Optional [str ]:
714
+ """Safely get an item of the Abstract Relation Data dictionary."""
715
+ try :
716
+ if result := self [key ]:
717
+ return result
718
+ except KeyError :
719
+ return default
720
+
721
+
615
722
class Data (ABC ):
616
723
"""Base relation data mainpulation (abstract) class."""
617
724
@@ -929,6 +1036,10 @@ def _delete_relation_data_without_secrets(
929
1036
# Public interface methods
930
1037
# Handling Relation Fields seamlessly, regardless if in databag or a Juju Secret
931
1038
1039
+ def as_dict (self , relation_id : int ) -> UserDict [str , str ]:
1040
+ """Dict behavior representation of the Abstract Data."""
1041
+ return DataDict (self , relation_id )
1042
+
932
1043
def get_relation (self , relation_name , relation_id ) -> Relation :
933
1044
"""Safe way of retrieving a relation."""
934
1045
relation = self ._model .get_relation (relation_name , relation_id )
@@ -1787,6 +1898,14 @@ def __init__(self, unit: Unit, *args, **kwargs):
1787
1898
self .local_unit = unit
1788
1899
self .component = unit
1789
1900
1901
+ def update_relation_data (self , relation_id : int , data : dict ) -> None :
1902
+ """This method makes no sense for a Other Peer Relation."""
1903
+ raise NotImplementedError ("It's not possible to update data of another unit." )
1904
+
1905
+ def delete_relation_data (self , relation_id : int , fields : List [str ]) -> None :
1906
+ """This method makes no sense for a Other Peer Relation."""
1907
+ raise NotImplementedError ("It's not possible to delete data of another unit." )
1908
+
1790
1909
1791
1910
class DataPeerOtherUnitEventHandlers (DataPeerEventHandlers ):
1792
1911
"""Requires-side of the relation."""
@@ -1809,18 +1928,18 @@ def __init__(
1809
1928
additional_secret_fields : Optional [List [str ]] = [],
1810
1929
secret_field_name : Optional [str ] = None ,
1811
1930
deleted_label : Optional [str ] = None ,
1812
- unique_key : str = "" ,
1813
1931
):
1814
- DataPeerData .__init__ (
1932
+ DataPeerOtherUnitData .__init__ (
1815
1933
self ,
1934
+ unit ,
1816
1935
charm .model ,
1817
1936
relation_name ,
1818
1937
extra_user_roles ,
1819
1938
additional_secret_fields ,
1820
1939
secret_field_name ,
1821
1940
deleted_label ,
1822
1941
)
1823
- DataPeerEventHandlers .__init__ (self , charm , self , unique_key )
1942
+ DataPeerOtherUnitEventHandlers .__init__ (self , charm , self )
1824
1943
1825
1944
1826
1945
# General events
0 commit comments