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 not self.ope 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        # return 'op : ' + (self.ope + ',').ljust(8, ' ') + ' path : ' + str(self.path)
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
174
175
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
303
304
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]
386
387
388class NtvOpError(Exception):
389    ''' NtvOp Exception'''
390    # pass
class NtvOp:
 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 not self.ope 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        # return 'op : ' + (self.ope + ',').ljust(8, ' ') + ' path : ' + str(self.path)
114
115    def __str__(self):
116        '''return json format'''
117        return json.dumps(self.json)
118
119    def __eq__(self, other):
120        ''' equal if all attributes are equal'''
121        return self.__class__.__name__ == other.__class__.__name__ and\
122            self.ope == other.ope and self.path == other.path and\
123            self.entity == other.entity and self.comment == other.comment and\
124            self.from_path == other.from_path
125
126    def __copy__(self):
127        ''' Copy all the data '''
128        cop = self.__class__(self)
129        return cop
130
131    @property
132    def json(self):
133        '''return the json-value representation (dict)'''
134        dic = {'op': self.ope, 'path': str(self.path), 'entity': self.entity,
135               'comment': self.comment, 'from': str(self.from_path)}
136        return {key: val for key, val in dic.items() if val and val != 'None'}
137
138    def exe(self, ntv):
139        '''applies the operation to the 'ntv' entity and return the resulting entity'''
140        from json_ntv.ntv import Ntv
141        ntv_res = copy(ntv)
142        idx = list(self.path)[-1]
143        p_path = self.path[:-1].fragment
144        path = self.path.fragment
145        if self.ope in ['move', 'copy', 'add']:
146            if self.ope == 'add' and self.entity:
147                ntv = Ntv.obj(self.entity)
148            elif self.ope == 'copy' and self.from_path:
149                ntv = copy(ntv_res[self.from_path.fragment])
150            elif self.ope == 'move' and self.from_path:
151                ntv = ntv_res[self.from_path.fragment]
152                del ntv_res[self.from_path[:-
153                                           1].fragment][list(self.from_path)[-1]]
154                ntv.parent = None
155            else:
156                raise NtvOpError('op is not correct')
157            if idx == '-':
158                ntv_res[p_path].append(ntv)
159            else:
160                ntv_res[p_path].insert(idx, ntv)
161        elif self.ope == 'test' and self.entity:
162            ntv = Ntv.obj(self.entity)
163            if not (idx == '-' and ntv in ntv_res[p_path]) and not (
164                    isinstance(idx, int) and ntv == ntv_res[path]):
165                raise NtvOpError('test is not correct')
166        elif self.ope == 'remove':
167            idx = list(self.path)[-1]
168            idx = len(ntv[p_path]) - 1 if idx == '-' else idx
169            ntv_res[p_path+'/'+str(idx)].remove(index=idx)
170        elif self.ope == 'replace' and self.entity:
171            ntv_res[path].replace(Ntv.obj(self.entity))
172        else:
173            raise NtvOpError('op add no result')
174        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

NtvOp(ope, path=None, entity=None, comment=None, from_path=None)
 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 not self.ope 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)
json

return the json-value representation (dict)

def exe(self, ntv):
138    def exe(self, ntv):
139        '''applies the operation to the 'ntv' entity and return the resulting entity'''
140        from json_ntv.ntv import Ntv
141        ntv_res = copy(ntv)
142        idx = list(self.path)[-1]
143        p_path = self.path[:-1].fragment
144        path = self.path.fragment
145        if self.ope in ['move', 'copy', 'add']:
146            if self.ope == 'add' and self.entity:
147                ntv = Ntv.obj(self.entity)
148            elif self.ope == 'copy' and self.from_path:
149                ntv = copy(ntv_res[self.from_path.fragment])
150            elif self.ope == 'move' and self.from_path:
151                ntv = ntv_res[self.from_path.fragment]
152                del ntv_res[self.from_path[:-
153                                           1].fragment][list(self.from_path)[-1]]
154                ntv.parent = None
155            else:
156                raise NtvOpError('op is not correct')
157            if idx == '-':
158                ntv_res[p_path].append(ntv)
159            else:
160                ntv_res[p_path].insert(idx, ntv)
161        elif self.ope == 'test' and self.entity:
162            ntv = Ntv.obj(self.entity)
163            if not (idx == '-' and ntv in ntv_res[p_path]) and not (
164                    isinstance(idx, int) and ntv == ntv_res[path]):
165                raise NtvOpError('test is not correct')
166        elif self.ope == 'remove':
167            idx = list(self.path)[-1]
168            idx = len(ntv[p_path]) - 1 if idx == '-' else idx
169            ntv_res[p_path+'/'+str(idx)].remove(index=idx)
170        elif self.ope == 'replace' and self.entity:
171            ntv_res[path].replace(Ntv.obj(self.entity))
172        else:
173            raise NtvOpError('op add no result')
174        return ntv_res

applies the operation to the 'ntv' entity and return the resulting entity

class NtvPatch:
177class NtvPatch:
178    ''' The NtvPatch class defines a sequence of operations to apply to an
179    NTV entity
180
181
182    *Attributes :*
183
184    - **list_op** : list - list of NtvOp to apply
185    - **comment**: string - description of the patch
186
187    *dynamic values (@property)*
188    - `json`
189
190    *instance method*
191    - `append`
192    - `exe`
193    '''
194
195    def __init__(self, list_op, comment=None):
196        '''constructor
197
198        *Parameters*
199
200        - **list_op**: list, dict, str, NtvOp, NtvPatch - list of operations
201            - list - list of op
202            - dict - json representation of a NtvPatch
203            - str - comment without op
204            - NtvOp - if only one op
205            - NtvPatch - copy of an existing NtvPatch
206        - **comment**: str (default None) - comment if not included in the list_op
207        '''
208        if isinstance(list_op, NtvPatch):
209            self.list_op = list_op.list_op
210            self.comment = list_op.comment
211            return
212        if isinstance(list_op, NtvOp):
213            self.comment = list_op.comment
214            self.list_op = [copy(list_op)]
215            self.list_op[0].comment = None
216            return
217        if isinstance(list_op, str):
218            self.comment = list_op
219            self.list_op = []
220            return
221        if isinstance(list_op, dict):
222            self.comment = list_op.get('comment', None)
223            lis = list_op.get('list-op', [])
224            self.list_op = [NtvOp(ope) for ope in lis]
225            return
226        list_op = [] if not list_op else list_op
227        self.list_op = [NtvOp(ope) for ope in list_op]
228        self.comment = comment
229        return
230
231    def __eq__(self, other):
232        ''' equal if list_op are equal'''
233        return self.__class__.__name__ == other.__class__.__name__ and\
234            self.list_op == other.list_op and self.comment == other.comment
235
236    def __copy__(self):
237        ''' Copy all the data '''
238        cop = self.__class__(self)
239        return cop
240
241    def __setitem__(self, ind, ope):
242        ''' replace op item in list_op at the `ind` row with `ope`'''
243        if ind < 0 or ind >= len(self):
244            raise NtvOpError("out of bounds")
245        self.list_op[ind] = ope
246
247    def __delitem__(self, ind):
248        '''remove op item at the `ind` row'''
249        if isinstance(ind, int):
250            self.list_op.pop(ind)
251        else:
252            self.list_op.pop(self.list_op.index(self[ind]))
253
254    def __len__(self):
255        ''' len of list_op'''
256        return len(self.list_op)
257
258    def __str__(self):
259        '''return comment and list of op in json-text format'''
260        return json.dumps(self.json)
261
262    def __repr__(self):
263        '''return classname, comment and list of op'''
264        rep = 'NtvPatch :' + (self.comment if self.comment else '')
265        for ind, ope in enumerate(self):
266            rep += '\n    op' + str(ind).ljust(3, ' ') + ' : ' + repr(ope)[5:]
267        return rep
268
269    def __contains__(self, item):
270        ''' return item is in the list of op'''
271        return item in self.list_op
272
273    def __iter__(self):
274        ''' iterator for list of op'''
275        return iter(self.list_op)
276
277    def __getitem__(self, selec):
278        ''' return op item in list of op'''
279        if selec is None or selec == [] or selec == () or selec == '':
280            return self
281        if isinstance(selec, (list, tuple)) and len(selec) == 1:
282            selec = selec[0]
283        if isinstance(selec, (list, tuple)):
284            return [self[i] for i in selec]
285        return self.list_op[selec]
286
287    def append(self, ope):
288        '''append 'ope' in the list of op'''
289        self.list_op.append(ope)
290
291    @property
292    def json(self):
293        '''return list of op in json-value format'''
294        return {'comment': self.comment,
295                'list-op': [ope.json for ope in self.list_op]}
296
297    def exe(self, ntv):
298        '''apply the included operations to NTV entity (ntv) and return
299        the resulting NTV entity'''
300        ntv_res = ntv
301        for ope in self:
302            ntv_res = ope.exe(ntv_res)
303        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

NtvPatch(list_op, comment=None)
195    def __init__(self, list_op, comment=None):
196        '''constructor
197
198        *Parameters*
199
200        - **list_op**: list, dict, str, NtvOp, NtvPatch - list of operations
201            - list - list of op
202            - dict - json representation of a NtvPatch
203            - str - comment without op
204            - NtvOp - if only one op
205            - NtvPatch - copy of an existing NtvPatch
206        - **comment**: str (default None) - comment if not included in the list_op
207        '''
208        if isinstance(list_op, NtvPatch):
209            self.list_op = list_op.list_op
210            self.comment = list_op.comment
211            return
212        if isinstance(list_op, NtvOp):
213            self.comment = list_op.comment
214            self.list_op = [copy(list_op)]
215            self.list_op[0].comment = None
216            return
217        if isinstance(list_op, str):
218            self.comment = list_op
219            self.list_op = []
220            return
221        if isinstance(list_op, dict):
222            self.comment = list_op.get('comment', None)
223            lis = list_op.get('list-op', [])
224            self.list_op = [NtvOp(ope) for ope in lis]
225            return
226        list_op = [] if not list_op else list_op
227        self.list_op = [NtvOp(ope) for ope in list_op]
228        self.comment = comment
229        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
def append(self, ope):
287    def append(self, ope):
288        '''append 'ope' in the list of op'''
289        self.list_op.append(ope)

append 'ope' in the list of op

json

return list of op in json-value format

def exe(self, ntv):
297    def exe(self, ntv):
298        '''apply the included operations to NTV entity (ntv) and return
299        the resulting NTV entity'''
300        ntv_res = ntv
301        for ope in self:
302            ntv_res = ope.exe(ntv_res)
303        return ntv_res

apply the included operations to NTV entity (ntv) and return the resulting NTV entity

class NtvPointer(builtins.list):
306class NtvPointer(list):
307    ''' The NtvPointer class defines methods to identify a node in a NTV entity
308
309    NtvPointer is child class of `list` class (no specific attribute)
310
311    *dynamic values (@property)*
312    - `fragment`
313
314    *static method*
315    - `split`
316    - `pointer_json`
317    - `pointer_list`
318
319    *instance method*
320    - `append`
321    '''
322
323    def __init__(self, pointer):
324        '''constructor
325
326        *Parameters*
327
328        - **pointer**: path from the root to the node
329            - list: list of int or str that identify a node
330            - NtvPointer: existing path to copy
331            - int: single level
332            - str: json_pointer
333        '''
334        if isinstance(pointer, (list, NtvPointer)):
335            super().__init__(pointer)
336        elif isinstance(pointer, (int, str)):
337            super().__init__(NtvPointer.pointer_list(pointer))
338
339    def __str__(self):
340        '''json-text representation of the NtvPointer'''
341        return NtvPointer.pointer_json(self)
342
343    def __getitem__(self, ind):
344        ''' return value record (value conversion)'''
345        return NtvPointer(super().__getitem__(ind))
346
347    @property
348    def fragment(self):
349        '''convert a NtvPointer into a fragment URI'''
350        return NtvPointer.pointer_json(self, fragment=True)
351
352    def append(self, child):
353        '''append a child pointer into a pointer '''
354        self += NtvPointer(child)
355
356    @staticmethod
357    def split(path):
358        '''return a tuple with the last pointer of the path
359        and the path without the last pointer'''
360        pointer = NtvPointer(path)
361        if not pointer:
362            return (None, None)
363        return (NtvPointer(pointer[-1]), NtvPointer(pointer[:-1]))
364
365    @staticmethod
366    def pointer_json(list_pointer, fragment=False):
367        '''convert a list of pointer into a json_pointer
368
369        *Parameters*
370
371        - **fragment**: Boolean (default False) - if True, insert '#' at the first place
372        '''
373        json_p = '' if not fragment else '#'
374        for name in list_pointer:
375            json_p += str(name).replace('~', '~0').replace('/', '~1') + '/'
376        return json_p[:-1]
377
378    @staticmethod
379    def pointer_list(json_pointer):
380        '''convert a json_pointer string into a pointer list'''
381        json_pointer = str(json_pointer)
382        split_pointer = json_pointer.split('/')
383        if len(split_pointer) == 0:
384            return []
385        return [int(nam) if nam.isdigit() else nam.replace('~1', '/').replace('~0', '/')
386                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

NtvPointer(pointer)
323    def __init__(self, pointer):
324        '''constructor
325
326        *Parameters*
327
328        - **pointer**: path from the root to the node
329            - list: list of int or str that identify a node
330            - NtvPointer: existing path to copy
331            - int: single level
332            - str: json_pointer
333        '''
334        if isinstance(pointer, (list, NtvPointer)):
335            super().__init__(pointer)
336        elif isinstance(pointer, (int, str)):
337            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
fragment

convert a NtvPointer into a fragment URI

def append(self, child):
352    def append(self, child):
353        '''append a child pointer into a pointer '''
354        self += NtvPointer(child)

append a child pointer into a pointer

@staticmethod
def split(path):
356    @staticmethod
357    def split(path):
358        '''return a tuple with the last pointer of the path
359        and the path without the last pointer'''
360        pointer = NtvPointer(path)
361        if not pointer:
362            return (None, None)
363        return (NtvPointer(pointer[-1]), NtvPointer(pointer[:-1]))

return a tuple with the last pointer of the path and the path without the last pointer

@staticmethod
def pointer_json(list_pointer, fragment=False):
365    @staticmethod
366    def pointer_json(list_pointer, fragment=False):
367        '''convert a list of pointer into a json_pointer
368
369        *Parameters*
370
371        - **fragment**: Boolean (default False) - if True, insert '#' at the first place
372        '''
373        json_p = '' if not fragment else '#'
374        for name in list_pointer:
375            json_p += str(name).replace('~', '~0').replace('/', '~1') + '/'
376        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
@staticmethod
def pointer_list(json_pointer):
378    @staticmethod
379    def pointer_list(json_pointer):
380        '''convert a json_pointer string into a pointer list'''
381        json_pointer = str(json_pointer)
382        split_pointer = json_pointer.split('/')
383        if len(split_pointer) == 0:
384            return []
385        return [int(nam) if nam.isdigit() else nam.replace('~1', '/').replace('~0', '/')
386                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
class NtvOpError(builtins.Exception):
389class NtvOpError(Exception):
390    ''' NtvOp Exception'''
391    # pass

NtvOp Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback