1010from typing import Any , Iterable , Set
1111from urllib .parse import quote_plus , urlencode
1212
13- from collections .abc import MutableMapping
13+ from collections .abc import MutableMapping , MutableSequence
1414from deprecated import deprecated
1515
1616from c8y_api ._base_api import CumulocityRestApi
1717from c8y_api .model ._util import _DateUtil , _StringUtil , _QueryUtil
1818
1919
20- class _DictWrapper (MutableMapping ):
20+ class _DictWrapper (MutableMapping , dict ):
2121
2222 def __init__ (self , dictionary : dict , on_update = None ):
2323 self .__dict__ ['_property_items' ] = dictionary
@@ -29,10 +29,16 @@ def has(self, name: str):
2929
3030 def __getitem__ (self , name ):
3131 item = self .__dict__ ['_property_items' ][name ]
32- return item if not isinstance (item , dict ) else _DictWrapper (item , self .__dict__ ['_property_on_update' ])
32+ if isinstance (item , dict ):
33+ return _DictWrapper (item , self .__dict__ ['_property_on_update' ])
34+ if isinstance (item , list ):
35+ return _ListWrapper (item , self .__dict__ ['_property_on_update' ])
36+ return item
3337
3438 def __setitem__ (self , name , value ):
3539 self .__dict__ ['_property_items' ][name ] = value
40+ if self .__dict__ ['_property_on_update' ]:
41+ self .__dict__ ['_property_on_update' ]()
3642
3743 def __delitem__ (self , _ ):
3844 raise NotImplementedError
@@ -52,13 +58,53 @@ def __getattr__(self, name):
5258 ) from None
5359
5460 def __setattr__ (self , name , value ):
55- if self .__dict__ ['_property_on_update' ]:
56- self .__dict__ ['_property_on_update' ]()
5761 self [name ] = value
5862
5963 def __str__ (self ):
6064 return self .__dict__ ['_property_items' ].__str__ ()
6165
66+ class _ListWrapper (MutableSequence , list ):
67+
68+ def __init__ (self , values : list , on_update = None ):
69+ self .__dict__ ['_property_items' ] = values
70+ self .__dict__ ['_property_on_update' ] = on_update
71+
72+ def __getitem__ (self , i ):
73+ item = self .__dict__ ['_property_items' ][i ]
74+ if isinstance (item , dict ):
75+ return _DictWrapper (item , self .__dict__ ['_property_on_update' ])
76+ if isinstance (item , list ):
77+ return _ListWrapper (item , self .__dict__ ['_property_on_update' ])
78+ return item
79+
80+ def __setitem__ (self , i , value ):
81+ self .__dict__ ['_property_items' ][i ] = value
82+ if self .__dict__ ['_property_on_update' ]:
83+ self .__dict__ ['_property_on_update' ]()
84+
85+ def __delitem__ (self , i ):
86+ del self .__dict__ ['_property_items' ][i ]
87+ if self .__dict__ ['_property_on_update' ]:
88+ self .__dict__ ['_property_on_update' ]()
89+
90+ def __len__ (self ):
91+ return len (self .__dict__ ['_property_items' ])
92+
93+ # def append(self, value):
94+ # self.__dict__['_property_items'].append(value)
95+ # if self.__dict__['_property_on_update']:
96+ # self.__dict__['_property_on_update']()
97+
98+ def insert (self , i , value ):
99+ self .__dict__ ['_property_items' ].insert (i , value )
100+ if self .__dict__ ['_property_on_update' ]:
101+ self .__dict__ ['_property_on_update' ]()
102+
103+ # def extend(self, other):
104+ # self.__dict__['_property_items'].extend(other)
105+ # if self.__dict__['_property_on_update']:
106+ # self.__dict__['_property_on_update']()
107+
62108
63109class CumulocityObject :
64110 """Base class for all Cumulocity database objects."""
@@ -355,10 +401,13 @@ def __getitem__(self, name: str):
355401 # it is ensured that the same access behaviour is ensured on all levels.
356402 # All updated anywhere within the dictionary tree will be reported as an update
357403 # to this instance.
358- # If the element is not a dictionary, it can be returned directly
404+ # If the element is not a dictionary or a list , it can be returned directly
359405 item = self .fragments [name ]
360- return item if not isinstance (item , dict ) else \
361- _DictWrapper (self .fragments [name ], lambda : self ._signal_updated_fragment (name ))
406+ if isinstance (item , dict ):
407+ return _DictWrapper (self .fragments [name ], lambda : self ._signal_updated_fragment (name ))
408+ if isinstance (item , list ):
409+ return _ListWrapper (self .fragments [name ], lambda : self ._signal_updated_fragment (name ))
410+ return item
362411
363412 def __getattr__ (self , name : str ):
364413 """ Get the value of a custom fragment.
@@ -372,10 +421,12 @@ def __getattr__(self, name: str):
372421 if name in self :
373422 return self [name ]
374423 pascal_name = _StringUtil .to_pascal_case (name )
424+ if pascal_name == name :
425+ raise AttributeError (f"'{ type (self ).__name__ } ' object has no attribute '{ name } '." ) from None
375426 if pascal_name in self :
376427 return self [pascal_name ]
377428 raise AttributeError (
378- f"'{ type (self ).__name__ } ' object has no attribute '{ name } ' or '{ pascal_name } '"
429+ f"'{ type (self ).__name__ } ' object has no attribute '{ name } ' or '{ pascal_name } '. "
379430 ) from None
380431
381432 def _setattr_ (self , name , value ):
@@ -574,14 +625,14 @@ def multi(*xs):
574625 'q' : q ,
575626 'query' : query ,
576627 'type' : type ,
577- 'name' : f"' { _QueryUtil .encode_odata_query_value (name )} '" if name else None ,
628+ 'name' : _QueryUtil .encode_odata_text_value (name )if name else None ,
578629 'owner' : owner ,
579630 'source' : source ,
580631 'fragmentType' : fragment ,
581632 'deviceId' : device_id ,
582633 'agentId' : agent_id ,
583634 'bulkId' : bulk_id ,
584- 'text' : f"' { _QueryUtil .encode_odata_query_value (text )} '" if text else None ,
635+ 'text' : _QueryUtil .encode_odata_text_value (text ) if text else None ,
585636 'ids' : ',' .join (str (i ) for i in ids ) if ids else None ,
586637 'bulkOperationId' : bulk_id ,
587638 'dateFrom' : date_from ,
0 commit comments