NTV.json_ntv.ntv_patch
Created on Sept 10 2023
@author: Philippe@loco-labs.io
The ntv_patch
module is part of the NTV.json_ntv
package (specification document).
It contains the classes NtvOp
, NtvPatch
, NtvPointer
1 - NtvPointer
NtvPointer defines a string syntax for identifying a specific node within a Ntv tree.
It is the transposition of JSON Pointer defined in RFC6901.
2 - NTV Patch and NtvOp
NTV Patch is a transposition of JSON Patch defined in RFC6902.
NTV Patch is a format for expressing a sequence of operations to be applied to a target NTV entity.
Each operation is defined by the NtvOp object
This format is also potentially useful in cases where it is necessary to make partial updates on an NTV entity.
The representation of an NtvPatch is a JSON-Array of NtvOp that can be added to an NTV entity (e.g. NtvComment - comments and change management).
The representation of an NtvOp is a JSON-Object
3 - Example
[
{'op': 'add', 'path': '/0/liste/0', 'entity': {'new value': 51}}
{'op': 'test', 'path': '/0/1/-', 'entity': {'new value': 51}}
{'op': 'remove', 'path': '/0/1/-'}
]
1# -*- coding: utf-8 -*- 2""" 3Created on Sept 10 2023 4 5@author: Philippe@loco-labs.io 6 7The `ntv_patch` module is part of the `NTV.json_ntv` package ([specification document]( 8https://loco-philippe.github.io/ES/JSON%20semantic%20format%20(JSON-NTV).htm)). 9 10It contains the classes `NtvOp`, `NtvPatch`, `NtvPointer` 11 12# 1 - NtvPointer 13 14NtvPointer defines a string syntax for identifying a specific node within a 15Ntv tree. 16 17It is the transposition of JSON Pointer defined in RFC6901. 18 19# 2 - NTV Patch and NtvOp 20 21NTV Patch is a transposition of JSON Patch defined in RFC6902. 22 23NTV Patch is a format for expressing a sequence of operations to be applied to a 24target NTV entity. 25 26Each operation is defined by the NtvOp object 27 28This format is also potentially useful in cases where it is necessary to 29make partial updates on an NTV entity. 30 31The representation of an NtvPatch is a JSON-Array of NtvOp that can be added to an NTV 32entity (e.g. NtvComment - comments and change management). 33 34The representation of an NtvOp is a JSON-Object 35 36# 3 - Example 37 38``` 39 [ 40 {'op': 'add', 'path': '/0/liste/0', 'entity': {'new value': 51}} 41 {'op': 'test', 'path': '/0/1/-', 'entity': {'new value': 51}} 42 {'op': 'remove', 'path': '/0/1/-'} 43 ] 44``` 45 46""" 47import json 48from copy import copy 49 50OPERATIONS = ['add', 'test', 'move', 'remove', 'copy', 'replace'] 51 52 53class NtvOp: 54 ''' The NtvOp class defines operations to apply to an NTV entity 55 56 *Attributes :* 57 58 - **op** : string - type of operation to apply 59 - **entity**: Ntv - entity subject to the operation 60 - **comment**: string - description of the operation 61 - **path**: string - NtvPointer to the child entity concerned by the op 62 - **from_path**: string - NtvPointer to the origin path (copy and move) 63 64 *dynamic values (@property)* 65 - `json` 66 67 *instance method* 68 - `exe` 69 ''' 70 71 def __init__(self, ope, path=None, entity=None, comment=None, from_path=None): 72 '''constructor 73 74 *Parameters* 75 76 - **ope**: str or dict - operation (str) or set of attributes (dict) 77 - **path**: str (default None) - Json of NtvPointer to the child entity 78 concerned by the ope 79 - **entity**: Json-value (default None) - NTV entity 80 - **comment** str (default None) - comment about ope 81 - **from_path**: str (default None) - Json of NtvPointer to the origin 82 path (copy and move) 83 ''' 84 ope = ope.json if isinstance(ope, NtvOp) else ope 85 if isinstance(ope, str): 86 self.ope = None 87 self.entity = None 88 self.comment = ope 89 self.from_path = [] 90 self.path = [] 91 return 92 dic = isinstance(ope, dict) 93 self.ope = ope.get('op') if dic else ope 94 self.entity = ope.get('entity') if dic else entity 95 self.comment = ope.get('comment') if dic else comment 96 self.from_path = NtvPointer( 97 ope.get('from')) if dic else NtvPointer(from_path) 98 self.path = NtvPointer(ope.get('path')) if dic else NtvPointer(path) 99 if self.ope and (not self.path or self.ope not in OPERATIONS): 100 raise NtvOpError('path or op is not correct') 101 102 def __repr__(self): 103 '''return the op and the path''' 104 txt = '' 105 if self.ope: 106 txt += 'op : ' + (self.ope + ',').ljust(8, ' ') 107 if self.path: 108 txt += ' path : ' + str(self.path) 109 if self.comment: 110 txt += self.comment 111 return txt 112 113 def __str__(self): 114 '''return json format''' 115 return json.dumps(self.json) 116 117 def __eq__(self, other): 118 ''' equal if all attributes are equal''' 119 return self.__class__.__name__ == other.__class__.__name__ and\ 120 self.ope == other.ope and self.path == other.path and\ 121 self.entity == other.entity and self.comment == other.comment and\ 122 self.from_path == other.from_path 123 124 def __copy__(self): 125 ''' Copy all the data ''' 126 cop = self.__class__(self) 127 return cop 128 129 @property 130 def json(self): 131 '''return the json-value representation (dict)''' 132 dic = {'op': self.ope, 'path': str(self.path), 'entity': self.entity, 133 'comment': self.comment, 'from': str(self.from_path)} 134 return {key: val for key, val in dic.items() if val and val != 'None'} 135 136 def exe(self, ntv): 137 '''applies the operation to the 'ntv' entity and return the resulting entity''' 138 from json_ntv.ntv import Ntv 139 ntv_res = copy(ntv) 140 idx = list(self.path)[-1] 141 p_path = self.path[:-1].fragment 142 path = self.path.fragment 143 if self.ope in ['move', 'copy', 'add']: 144 if self.ope == 'add' and self.entity: 145 ntv = Ntv.obj(self.entity) 146 elif self.ope == 'copy' and self.from_path: 147 ntv = copy(ntv_res[self.from_path.fragment]) 148 elif self.ope == 'move' and self.from_path: 149 ntv = ntv_res[self.from_path.fragment] 150 del ntv_res[self.from_path[:- 151 1].fragment][list(self.from_path)[-1]] 152 ntv.parent = None 153 else: 154 raise NtvOpError('op is not correct') 155 if idx == '-': 156 ntv_res[p_path].append(ntv) 157 else: 158 ntv_res[p_path].insert(idx, ntv) 159 elif self.ope == 'test' and self.entity: 160 ntv = Ntv.obj(self.entity) 161 if not (idx == '-' and ntv in ntv_res[p_path]) and not ( 162 isinstance(idx, int) and ntv == ntv_res[path]): 163 raise NtvOpError('test is not correct') 164 elif self.ope == 'remove': 165 idx = list(self.path)[-1] 166 idx = len(ntv[p_path]) - 1 if idx == '-' else idx 167 ntv_res[p_path+'/'+str(idx)].remove(index=idx) 168 elif self.ope == 'replace' and self.entity: 169 ntv_res[path].replace(Ntv.obj(self.entity)) 170 else: 171 raise NtvOpError('op add no result') 172 return ntv_res 173 174 175class NtvPatch: 176 ''' The NtvPatch class defines a sequence of operations to apply to an 177 NTV entity 178 179 180 *Attributes :* 181 182 - **list_op** : list - list of NtvOp to apply 183 - **comment**: string - description of the patch 184 185 *dynamic values (@property)* 186 - `json` 187 188 *instance method* 189 - `append` 190 - `exe` 191 ''' 192 193 def __init__(self, list_op, comment=None): 194 '''constructor 195 196 *Parameters* 197 198 - **list_op**: list, dict, str, NtvOp, NtvPatch - list of operations 199 - list - list of op 200 - dict - json representation of a NtvPatch 201 - str - comment without op 202 - NtvOp - if only one op 203 - NtvPatch - copy of an existing NtvPatch 204 - **comment**: str (default None) - comment if not included in the list_op 205 ''' 206 if isinstance(list_op, NtvPatch): 207 self.list_op = list_op.list_op 208 self.comment = list_op.comment 209 return 210 if isinstance(list_op, NtvOp): 211 self.comment = list_op.comment 212 self.list_op = [copy(list_op)] 213 self.list_op[0].comment = None 214 return 215 if isinstance(list_op, str): 216 self.comment = list_op 217 self.list_op = [] 218 return 219 if isinstance(list_op, dict): 220 self.comment = list_op.get('comment', None) 221 lis = list_op.get('list-op', []) 222 self.list_op = [NtvOp(ope) for ope in lis] 223 return 224 list_op = [] if not list_op else list_op 225 self.list_op = [NtvOp(ope) for ope in list_op] 226 self.comment = comment 227 return 228 229 def __eq__(self, other): 230 ''' equal if list_op are equal''' 231 return self.__class__.__name__ == other.__class__.__name__ and\ 232 self.list_op == other.list_op and self.comment == other.comment 233 234 def __copy__(self): 235 ''' Copy all the data ''' 236 cop = self.__class__(self) 237 return cop 238 239 def __setitem__(self, ind, ope): 240 ''' replace op item in list_op at the `ind` row with `ope`''' 241 if ind < 0 or ind >= len(self): 242 raise NtvOpError("out of bounds") 243 self.list_op[ind] = ope 244 245 def __delitem__(self, ind): 246 '''remove op item at the `ind` row''' 247 if isinstance(ind, int): 248 self.list_op.pop(ind) 249 else: 250 self.list_op.pop(self.list_op.index(self[ind])) 251 252 def __len__(self): 253 ''' len of list_op''' 254 return len(self.list_op) 255 256 def __str__(self): 257 '''return comment and list of op in json-text format''' 258 return json.dumps(self.json) 259 260 def __repr__(self): 261 '''return classname, comment and list of op''' 262 rep = 'NtvPatch :' + (self.comment if self.comment else '') 263 for ind, ope in enumerate(self): 264 rep += '\n op' + str(ind).ljust(3, ' ') + ' : ' + repr(ope)[5:] 265 return rep 266 267 def __contains__(self, item): 268 ''' return item is in the list of op''' 269 return item in self.list_op 270 271 def __iter__(self): 272 ''' iterator for list of op''' 273 return iter(self.list_op) 274 275 def __getitem__(self, selec): 276 ''' return op item in list of op''' 277 if selec is None or selec == [] or selec == () or selec == '': 278 return self 279 if isinstance(selec, (list, tuple)) and len(selec) == 1: 280 selec = selec[0] 281 if isinstance(selec, (list, tuple)): 282 return [self[i] for i in selec] 283 return self.list_op[selec] 284 285 def append(self, ope): 286 '''append 'ope' in the list of op''' 287 self.list_op.append(ope) 288 289 @property 290 def json(self): 291 '''return list of op in json-value format''' 292 return {'comment': self.comment, 293 'list-op': [ope.json for ope in self.list_op]} 294 295 def exe(self, ntv): 296 '''apply the included operations to NTV entity (ntv) and return 297 the resulting NTV entity''' 298 ntv_res = ntv 299 for ope in self: 300 ntv_res = ope.exe(ntv_res) 301 return ntv_res 302 303 304class NtvPointer(list): 305 ''' The NtvPointer class defines methods to identify a node in a NTV entity 306 307 NtvPointer is child class of `list` class (no specific attribute) 308 309 *dynamic values (@property)* 310 - `fragment` 311 312 *static method* 313 - `split` 314 - `pointer_json` 315 - `pointer_list` 316 317 *instance method* 318 - `append` 319 ''' 320 321 def __init__(self, pointer): 322 '''constructor 323 324 *Parameters* 325 326 - **pointer**: path from the root to the node 327 - list: list of int or str that identify a node 328 - NtvPointer: existing path to copy 329 - int: single level 330 - str: json_pointer 331 ''' 332 if isinstance(pointer, (list, NtvPointer)): 333 super().__init__(pointer) 334 elif isinstance(pointer, (int, str)): 335 super().__init__(NtvPointer.pointer_list(pointer)) 336 337 def __str__(self): 338 '''json-text representation of the NtvPointer''' 339 return NtvPointer.pointer_json(self) 340 341 def __getitem__(self, ind): 342 ''' return value record (value conversion)''' 343 return NtvPointer(super().__getitem__(ind)) 344 345 @property 346 def fragment(self): 347 '''convert a NtvPointer into a fragment URI''' 348 return NtvPointer.pointer_json(self, fragment=True) 349 350 def append(self, child): 351 '''append a child pointer into a pointer ''' 352 self += NtvPointer(child) 353 354 @staticmethod 355 def split(path): 356 '''return a tuple with the last pointer of the path 357 and the path without the last pointer''' 358 pointer = NtvPointer(path) 359 if not pointer: 360 return (None, None) 361 return (NtvPointer(pointer[-1]), NtvPointer(pointer[:-1])) 362 363 @staticmethod 364 def pointer_json(list_pointer, fragment=False): 365 '''convert a list of pointer into a json_pointer 366 367 *Parameters* 368 369 - **fragment**: Boolean (default False) - if True, insert '#' at the first place 370 ''' 371 json_p = '' if not fragment else '#' 372 for name in list_pointer: 373 json_p += str(name).replace('~', '~0').replace('/', '~1') + '/' 374 return json_p[:-1] 375 376 @staticmethod 377 def pointer_list(json_pointer): 378 '''convert a json_pointer string into a pointer list''' 379 json_pointer = str(json_pointer) 380 split_pointer = json_pointer.split('/') 381 if len(split_pointer) == 0: 382 return [] 383 return [int(nam) if nam.isdigit() else nam.replace('~1', '/').replace('~0', '/') 384 for nam in split_pointer] 385 386 387class NtvOpError(Exception): 388 ''' NtvOp Exception''' 389 # pass
54class NtvOp: 55 ''' The NtvOp class defines operations to apply to an NTV entity 56 57 *Attributes :* 58 59 - **op** : string - type of operation to apply 60 - **entity**: Ntv - entity subject to the operation 61 - **comment**: string - description of the operation 62 - **path**: string - NtvPointer to the child entity concerned by the op 63 - **from_path**: string - NtvPointer to the origin path (copy and move) 64 65 *dynamic values (@property)* 66 - `json` 67 68 *instance method* 69 - `exe` 70 ''' 71 72 def __init__(self, ope, path=None, entity=None, comment=None, from_path=None): 73 '''constructor 74 75 *Parameters* 76 77 - **ope**: str or dict - operation (str) or set of attributes (dict) 78 - **path**: str (default None) - Json of NtvPointer to the child entity 79 concerned by the ope 80 - **entity**: Json-value (default None) - NTV entity 81 - **comment** str (default None) - comment about ope 82 - **from_path**: str (default None) - Json of NtvPointer to the origin 83 path (copy and move) 84 ''' 85 ope = ope.json if isinstance(ope, NtvOp) else ope 86 if isinstance(ope, str): 87 self.ope = None 88 self.entity = None 89 self.comment = ope 90 self.from_path = [] 91 self.path = [] 92 return 93 dic = isinstance(ope, dict) 94 self.ope = ope.get('op') if dic else ope 95 self.entity = ope.get('entity') if dic else entity 96 self.comment = ope.get('comment') if dic else comment 97 self.from_path = NtvPointer( 98 ope.get('from')) if dic else NtvPointer(from_path) 99 self.path = NtvPointer(ope.get('path')) if dic else NtvPointer(path) 100 if self.ope and (not self.path or self.ope not in OPERATIONS): 101 raise NtvOpError('path or op is not correct') 102 103 def __repr__(self): 104 '''return the op and the path''' 105 txt = '' 106 if self.ope: 107 txt += 'op : ' + (self.ope + ',').ljust(8, ' ') 108 if self.path: 109 txt += ' path : ' + str(self.path) 110 if self.comment: 111 txt += self.comment 112 return txt 113 114 def __str__(self): 115 '''return json format''' 116 return json.dumps(self.json) 117 118 def __eq__(self, other): 119 ''' equal if all attributes are equal''' 120 return self.__class__.__name__ == other.__class__.__name__ and\ 121 self.ope == other.ope and self.path == other.path and\ 122 self.entity == other.entity and self.comment == other.comment and\ 123 self.from_path == other.from_path 124 125 def __copy__(self): 126 ''' Copy all the data ''' 127 cop = self.__class__(self) 128 return cop 129 130 @property 131 def json(self): 132 '''return the json-value representation (dict)''' 133 dic = {'op': self.ope, 'path': str(self.path), 'entity': self.entity, 134 'comment': self.comment, 'from': str(self.from_path)} 135 return {key: val for key, val in dic.items() if val and val != 'None'} 136 137 def exe(self, ntv): 138 '''applies the operation to the 'ntv' entity and return the resulting entity''' 139 from json_ntv.ntv import Ntv 140 ntv_res = copy(ntv) 141 idx = list(self.path)[-1] 142 p_path = self.path[:-1].fragment 143 path = self.path.fragment 144 if self.ope in ['move', 'copy', 'add']: 145 if self.ope == 'add' and self.entity: 146 ntv = Ntv.obj(self.entity) 147 elif self.ope == 'copy' and self.from_path: 148 ntv = copy(ntv_res[self.from_path.fragment]) 149 elif self.ope == 'move' and self.from_path: 150 ntv = ntv_res[self.from_path.fragment] 151 del ntv_res[self.from_path[:- 152 1].fragment][list(self.from_path)[-1]] 153 ntv.parent = None 154 else: 155 raise NtvOpError('op is not correct') 156 if idx == '-': 157 ntv_res[p_path].append(ntv) 158 else: 159 ntv_res[p_path].insert(idx, ntv) 160 elif self.ope == 'test' and self.entity: 161 ntv = Ntv.obj(self.entity) 162 if not (idx == '-' and ntv in ntv_res[p_path]) and not ( 163 isinstance(idx, int) and ntv == ntv_res[path]): 164 raise NtvOpError('test is not correct') 165 elif self.ope == 'remove': 166 idx = list(self.path)[-1] 167 idx = len(ntv[p_path]) - 1 if idx == '-' else idx 168 ntv_res[p_path+'/'+str(idx)].remove(index=idx) 169 elif self.ope == 'replace' and self.entity: 170 ntv_res[path].replace(Ntv.obj(self.entity)) 171 else: 172 raise NtvOpError('op add no result') 173 return ntv_res
The NtvOp class defines operations to apply to an NTV entity
Attributes :
- op : string - type of operation to apply
- entity: Ntv - entity subject to the operation
- comment: string - description of the operation
- path: string - NtvPointer to the child entity concerned by the op
- from_path: string - NtvPointer to the origin path (copy and move)
dynamic values (@property)
instance method
72 def __init__(self, ope, path=None, entity=None, comment=None, from_path=None): 73 '''constructor 74 75 *Parameters* 76 77 - **ope**: str or dict - operation (str) or set of attributes (dict) 78 - **path**: str (default None) - Json of NtvPointer to the child entity 79 concerned by the ope 80 - **entity**: Json-value (default None) - NTV entity 81 - **comment** str (default None) - comment about ope 82 - **from_path**: str (default None) - Json of NtvPointer to the origin 83 path (copy and move) 84 ''' 85 ope = ope.json if isinstance(ope, NtvOp) else ope 86 if isinstance(ope, str): 87 self.ope = None 88 self.entity = None 89 self.comment = ope 90 self.from_path = [] 91 self.path = [] 92 return 93 dic = isinstance(ope, dict) 94 self.ope = ope.get('op') if dic else ope 95 self.entity = ope.get('entity') if dic else entity 96 self.comment = ope.get('comment') if dic else comment 97 self.from_path = NtvPointer( 98 ope.get('from')) if dic else NtvPointer(from_path) 99 self.path = NtvPointer(ope.get('path')) if dic else NtvPointer(path) 100 if self.ope and (not self.path or self.ope not in OPERATIONS): 101 raise NtvOpError('path or op is not correct')
constructor
Parameters
- ope: str or dict - operation (str) or set of attributes (dict)
- path: str (default None) - Json of NtvPointer to the child entity concerned by the ope
- entity: Json-value (default None) - NTV entity
- comment str (default None) - comment about ope
- from_path: str (default None) - Json of NtvPointer to the origin path (copy and move)
130 @property 131 def json(self): 132 '''return the json-value representation (dict)''' 133 dic = {'op': self.ope, 'path': str(self.path), 'entity': self.entity, 134 'comment': self.comment, 'from': str(self.from_path)} 135 return {key: val for key, val in dic.items() if val and val != 'None'}
return the json-value representation (dict)
137 def exe(self, ntv): 138 '''applies the operation to the 'ntv' entity and return the resulting entity''' 139 from json_ntv.ntv import Ntv 140 ntv_res = copy(ntv) 141 idx = list(self.path)[-1] 142 p_path = self.path[:-1].fragment 143 path = self.path.fragment 144 if self.ope in ['move', 'copy', 'add']: 145 if self.ope == 'add' and self.entity: 146 ntv = Ntv.obj(self.entity) 147 elif self.ope == 'copy' and self.from_path: 148 ntv = copy(ntv_res[self.from_path.fragment]) 149 elif self.ope == 'move' and self.from_path: 150 ntv = ntv_res[self.from_path.fragment] 151 del ntv_res[self.from_path[:- 152 1].fragment][list(self.from_path)[-1]] 153 ntv.parent = None 154 else: 155 raise NtvOpError('op is not correct') 156 if idx == '-': 157 ntv_res[p_path].append(ntv) 158 else: 159 ntv_res[p_path].insert(idx, ntv) 160 elif self.ope == 'test' and self.entity: 161 ntv = Ntv.obj(self.entity) 162 if not (idx == '-' and ntv in ntv_res[p_path]) and not ( 163 isinstance(idx, int) and ntv == ntv_res[path]): 164 raise NtvOpError('test is not correct') 165 elif self.ope == 'remove': 166 idx = list(self.path)[-1] 167 idx = len(ntv[p_path]) - 1 if idx == '-' else idx 168 ntv_res[p_path+'/'+str(idx)].remove(index=idx) 169 elif self.ope == 'replace' and self.entity: 170 ntv_res[path].replace(Ntv.obj(self.entity)) 171 else: 172 raise NtvOpError('op add no result') 173 return ntv_res
applies the operation to the 'ntv' entity and return the resulting entity
176class NtvPatch: 177 ''' The NtvPatch class defines a sequence of operations to apply to an 178 NTV entity 179 180 181 *Attributes :* 182 183 - **list_op** : list - list of NtvOp to apply 184 - **comment**: string - description of the patch 185 186 *dynamic values (@property)* 187 - `json` 188 189 *instance method* 190 - `append` 191 - `exe` 192 ''' 193 194 def __init__(self, list_op, comment=None): 195 '''constructor 196 197 *Parameters* 198 199 - **list_op**: list, dict, str, NtvOp, NtvPatch - list of operations 200 - list - list of op 201 - dict - json representation of a NtvPatch 202 - str - comment without op 203 - NtvOp - if only one op 204 - NtvPatch - copy of an existing NtvPatch 205 - **comment**: str (default None) - comment if not included in the list_op 206 ''' 207 if isinstance(list_op, NtvPatch): 208 self.list_op = list_op.list_op 209 self.comment = list_op.comment 210 return 211 if isinstance(list_op, NtvOp): 212 self.comment = list_op.comment 213 self.list_op = [copy(list_op)] 214 self.list_op[0].comment = None 215 return 216 if isinstance(list_op, str): 217 self.comment = list_op 218 self.list_op = [] 219 return 220 if isinstance(list_op, dict): 221 self.comment = list_op.get('comment', None) 222 lis = list_op.get('list-op', []) 223 self.list_op = [NtvOp(ope) for ope in lis] 224 return 225 list_op = [] if not list_op else list_op 226 self.list_op = [NtvOp(ope) for ope in list_op] 227 self.comment = comment 228 return 229 230 def __eq__(self, other): 231 ''' equal if list_op are equal''' 232 return self.__class__.__name__ == other.__class__.__name__ and\ 233 self.list_op == other.list_op and self.comment == other.comment 234 235 def __copy__(self): 236 ''' Copy all the data ''' 237 cop = self.__class__(self) 238 return cop 239 240 def __setitem__(self, ind, ope): 241 ''' replace op item in list_op at the `ind` row with `ope`''' 242 if ind < 0 or ind >= len(self): 243 raise NtvOpError("out of bounds") 244 self.list_op[ind] = ope 245 246 def __delitem__(self, ind): 247 '''remove op item at the `ind` row''' 248 if isinstance(ind, int): 249 self.list_op.pop(ind) 250 else: 251 self.list_op.pop(self.list_op.index(self[ind])) 252 253 def __len__(self): 254 ''' len of list_op''' 255 return len(self.list_op) 256 257 def __str__(self): 258 '''return comment and list of op in json-text format''' 259 return json.dumps(self.json) 260 261 def __repr__(self): 262 '''return classname, comment and list of op''' 263 rep = 'NtvPatch :' + (self.comment if self.comment else '') 264 for ind, ope in enumerate(self): 265 rep += '\n op' + str(ind).ljust(3, ' ') + ' : ' + repr(ope)[5:] 266 return rep 267 268 def __contains__(self, item): 269 ''' return item is in the list of op''' 270 return item in self.list_op 271 272 def __iter__(self): 273 ''' iterator for list of op''' 274 return iter(self.list_op) 275 276 def __getitem__(self, selec): 277 ''' return op item in list of op''' 278 if selec is None or selec == [] or selec == () or selec == '': 279 return self 280 if isinstance(selec, (list, tuple)) and len(selec) == 1: 281 selec = selec[0] 282 if isinstance(selec, (list, tuple)): 283 return [self[i] for i in selec] 284 return self.list_op[selec] 285 286 def append(self, ope): 287 '''append 'ope' in the list of op''' 288 self.list_op.append(ope) 289 290 @property 291 def json(self): 292 '''return list of op in json-value format''' 293 return {'comment': self.comment, 294 'list-op': [ope.json for ope in self.list_op]} 295 296 def exe(self, ntv): 297 '''apply the included operations to NTV entity (ntv) and return 298 the resulting NTV entity''' 299 ntv_res = ntv 300 for ope in self: 301 ntv_res = ope.exe(ntv_res) 302 return ntv_res
The NtvPatch class defines a sequence of operations to apply to an NTV entity
Attributes :
- list_op : list - list of NtvOp to apply
- comment: string - description of the patch
dynamic values (@property)
instance method
194 def __init__(self, list_op, comment=None): 195 '''constructor 196 197 *Parameters* 198 199 - **list_op**: list, dict, str, NtvOp, NtvPatch - list of operations 200 - list - list of op 201 - dict - json representation of a NtvPatch 202 - str - comment without op 203 - NtvOp - if only one op 204 - NtvPatch - copy of an existing NtvPatch 205 - **comment**: str (default None) - comment if not included in the list_op 206 ''' 207 if isinstance(list_op, NtvPatch): 208 self.list_op = list_op.list_op 209 self.comment = list_op.comment 210 return 211 if isinstance(list_op, NtvOp): 212 self.comment = list_op.comment 213 self.list_op = [copy(list_op)] 214 self.list_op[0].comment = None 215 return 216 if isinstance(list_op, str): 217 self.comment = list_op 218 self.list_op = [] 219 return 220 if isinstance(list_op, dict): 221 self.comment = list_op.get('comment', None) 222 lis = list_op.get('list-op', []) 223 self.list_op = [NtvOp(ope) for ope in lis] 224 return 225 list_op = [] if not list_op else list_op 226 self.list_op = [NtvOp(ope) for ope in list_op] 227 self.comment = comment 228 return
constructor
Parameters
- list_op: list, dict, str, NtvOp, NtvPatch - list of operations
- list - list of op
- dict - json representation of a NtvPatch
- str - comment without op
- NtvOp - if only one op
- NtvPatch - copy of an existing NtvPatch
- comment: str (default None) - comment if not included in the list_op
290 @property 291 def json(self): 292 '''return list of op in json-value format''' 293 return {'comment': self.comment, 294 'list-op': [ope.json for ope in self.list_op]}
return list of op in json-value format
296 def exe(self, ntv): 297 '''apply the included operations to NTV entity (ntv) and return 298 the resulting NTV entity''' 299 ntv_res = ntv 300 for ope in self: 301 ntv_res = ope.exe(ntv_res) 302 return ntv_res
apply the included operations to NTV entity (ntv) and return the resulting NTV entity
305class NtvPointer(list): 306 ''' The NtvPointer class defines methods to identify a node in a NTV entity 307 308 NtvPointer is child class of `list` class (no specific attribute) 309 310 *dynamic values (@property)* 311 - `fragment` 312 313 *static method* 314 - `split` 315 - `pointer_json` 316 - `pointer_list` 317 318 *instance method* 319 - `append` 320 ''' 321 322 def __init__(self, pointer): 323 '''constructor 324 325 *Parameters* 326 327 - **pointer**: path from the root to the node 328 - list: list of int or str that identify a node 329 - NtvPointer: existing path to copy 330 - int: single level 331 - str: json_pointer 332 ''' 333 if isinstance(pointer, (list, NtvPointer)): 334 super().__init__(pointer) 335 elif isinstance(pointer, (int, str)): 336 super().__init__(NtvPointer.pointer_list(pointer)) 337 338 def __str__(self): 339 '''json-text representation of the NtvPointer''' 340 return NtvPointer.pointer_json(self) 341 342 def __getitem__(self, ind): 343 ''' return value record (value conversion)''' 344 return NtvPointer(super().__getitem__(ind)) 345 346 @property 347 def fragment(self): 348 '''convert a NtvPointer into a fragment URI''' 349 return NtvPointer.pointer_json(self, fragment=True) 350 351 def append(self, child): 352 '''append a child pointer into a pointer ''' 353 self += NtvPointer(child) 354 355 @staticmethod 356 def split(path): 357 '''return a tuple with the last pointer of the path 358 and the path without the last pointer''' 359 pointer = NtvPointer(path) 360 if not pointer: 361 return (None, None) 362 return (NtvPointer(pointer[-1]), NtvPointer(pointer[:-1])) 363 364 @staticmethod 365 def pointer_json(list_pointer, fragment=False): 366 '''convert a list of pointer into a json_pointer 367 368 *Parameters* 369 370 - **fragment**: Boolean (default False) - if True, insert '#' at the first place 371 ''' 372 json_p = '' if not fragment else '#' 373 for name in list_pointer: 374 json_p += str(name).replace('~', '~0').replace('/', '~1') + '/' 375 return json_p[:-1] 376 377 @staticmethod 378 def pointer_list(json_pointer): 379 '''convert a json_pointer string into a pointer list''' 380 json_pointer = str(json_pointer) 381 split_pointer = json_pointer.split('/') 382 if len(split_pointer) == 0: 383 return [] 384 return [int(nam) if nam.isdigit() else nam.replace('~1', '/').replace('~0', '/') 385 for nam in split_pointer]
The NtvPointer class defines methods to identify a node in a NTV entity
NtvPointer is child class of list
class (no specific attribute)
dynamic values (@property)
static method
instance method
322 def __init__(self, pointer): 323 '''constructor 324 325 *Parameters* 326 327 - **pointer**: path from the root to the node 328 - list: list of int or str that identify a node 329 - NtvPointer: existing path to copy 330 - int: single level 331 - str: json_pointer 332 ''' 333 if isinstance(pointer, (list, NtvPointer)): 334 super().__init__(pointer) 335 elif isinstance(pointer, (int, str)): 336 super().__init__(NtvPointer.pointer_list(pointer))
constructor
Parameters
- pointer: path from the root to the node
- list: list of int or str that identify a node
- NtvPointer: existing path to copy
- int: single level
- str: json_pointer
346 @property 347 def fragment(self): 348 '''convert a NtvPointer into a fragment URI''' 349 return NtvPointer.pointer_json(self, fragment=True)
convert a NtvPointer into a fragment URI
351 def append(self, child): 352 '''append a child pointer into a pointer ''' 353 self += NtvPointer(child)
append a child pointer into a pointer
355 @staticmethod 356 def split(path): 357 '''return a tuple with the last pointer of the path 358 and the path without the last pointer''' 359 pointer = NtvPointer(path) 360 if not pointer: 361 return (None, None) 362 return (NtvPointer(pointer[-1]), NtvPointer(pointer[:-1]))
return a tuple with the last pointer of the path and the path without the last pointer
364 @staticmethod 365 def pointer_json(list_pointer, fragment=False): 366 '''convert a list of pointer into a json_pointer 367 368 *Parameters* 369 370 - **fragment**: Boolean (default False) - if True, insert '#' at the first place 371 ''' 372 json_p = '' if not fragment else '#' 373 for name in list_pointer: 374 json_p += str(name).replace('~', '~0').replace('/', '~1') + '/' 375 return json_p[:-1]
convert a list of pointer into a json_pointer
Parameters
- fragment: Boolean (default False) - if True, insert '#' at the first place
377 @staticmethod 378 def pointer_list(json_pointer): 379 '''convert a json_pointer string into a pointer list''' 380 json_pointer = str(json_pointer) 381 split_pointer = json_pointer.split('/') 382 if len(split_pointer) == 0: 383 return [] 384 return [int(nam) if nam.isdigit() else nam.replace('~1', '/').replace('~0', '/') 385 for nam in split_pointer]
convert a json_pointer string into a pointer list
Inherited Members
- builtins.list
- clear
- copy
- insert
- extend
- pop
- remove
- index
- count
- reverse
- sort
NtvOp Exception
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args