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
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
OPERATIONS = ['add', 'test', 'move', 'remove', 'copy', 'replace']
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
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

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)
ope
entity
comment
from_path
path
json
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)

def exe(self, ntv):
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

class NtvPatch:
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

NtvPatch(list_op, comment=None)
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
list_op
comment
def append(self, ope):
286    def append(self, ope):
287        '''append 'ope' in the list of op'''
288        self.list_op.append(ope)

append 'ope' in the list of op

json
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

def exe(self, ntv):
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

class NtvPointer(builtins.list):
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

NtvPointer(pointer)
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
fragment
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

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

append a child pointer into a pointer

@staticmethod
def split(path):
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

@staticmethod
def pointer_json(list_pointer, fragment=False):
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
@staticmethod
def pointer_list(json_pointer):
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
class NtvOpError(builtins.Exception):
388class NtvOpError(Exception):
389    ''' NtvOp Exception'''
390    # pass

NtvOp Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args